or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

http-methods.mdindex.mdpath-matching.mdresponse-generation.md
tile.json

response-generation.mddocs/

Response Generation

Complete HTTP status code constants and response builders for creating well-formed HTTP responses. The DSL provides fluent APIs for all standard HTTP status codes with appropriate response body and header handling.

Capabilities

Response Generator Traits

Base traits for building HTTP responses with different characteristics.

/**
 * Base trait for all response generators
 */
trait ResponseGenerator extends Any {
  def status: Status
}

/**
 * Generator for responses without body content
 */
trait EmptyResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
  def apply()(implicit F: Applicative[F]): F[Response[G]]
  def headers(header: Header.ToRaw, _headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
}

/**
 * Generator for responses with optional body content
 */
trait EntityResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
  def liftG: FunctionK[G, F]
  
  def apply()(implicit F: Applicative[F]): F[Response[G]]
  def apply[A](body: G[A])(implicit F: Monad[F], w: EntityEncoder[G, A]): F[Response[G]]
  def apply[A](body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
  def headers(header: Header.ToRaw, _headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
}

/**
 * Generator for redirect responses requiring Location header
 */
trait LocationResponseGenerator[F[_], G[_]] extends Any with EntityResponseGenerator[F, G] {
  def apply(location: Location)(implicit F: Applicative[F]): F[Response[G]]
  def apply[A](location: Location, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
}

/**
 * Generator for 401 Unauthorized responses requiring WWW-Authenticate header
 */
trait WwwAuthenticateResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
  def apply(authenticate: `WWW-Authenticate`, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
  def apply[A](authenticate: `WWW-Authenticate`, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
}

/**
 * Generator for 405 Method Not Allowed responses requiring Allow header
 */
trait AllowResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
  def apply(allow: Allow, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
  def apply[A](allow: Allow, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
}

Success Responses (2xx)

Status codes indicating successful request processing.

// 2xx Success Status Codes
val Ok: Status.Ok.type                                                    // 200
val Created: Status.Created.type                                          // 201  
val Accepted: Status.Accepted.type                                        // 202
val NonAuthoritativeInformation: Status.NonAuthoritativeInformation.type  // 203
val NoContent: Status.NoContent.type                                      // 204
val ResetContent: Status.ResetContent.type                                // 205
val PartialContent: Status.PartialContent.type                            // 206
val MultiStatus: Status.MultiStatus.type                                  // 207
val AlreadyReported: Status.AlreadyReported.type                          // 208
val IMUsed: Status.IMUsed.type                                            // 226

Usage Examples:

import org.http4s.dsl.io._
import cats.effect.IO

val routes = HttpRoutes.of[IO] {
  // Simple OK response
  case GET -> Root / "hello" =>
    Ok("Hello, World!")
    
  // Created with response body
  case POST -> Root / "users" =>
    Created("User created successfully")
    
  // No content response
  case DELETE -> Root / "users" / IntVar(id) =>
    NoContent()
    
  // Response with custom headers
  case GET -> Root / "data" =>
    Ok("Response data", Header("X-Custom", "value"))
    
  // JSON response (with appropriate EntityEncoder)
  case GET -> Root / "api" / "user" / IntVar(id) =>
    val user = User(id, "John Doe")
    Ok(user.asJson)
}

Redirection Responses (3xx)

Status codes for URL redirection with automatic Location header handling.

// 3xx Redirection Status Codes
val MultipleChoices: Status.MultipleChoices.type        // 300
val MovedPermanently: Status.MovedPermanently.type      // 301
val Found: Status.Found.type                            // 302
val SeeOther: Status.SeeOther.type                      // 303
val NotModified: Status.NotModified.type                // 304
val TemporaryRedirect: Status.TemporaryRedirect.type    // 307
val PermanentRedirect: Status.PermanentRedirect.type    // 308

Usage Examples:

import org.http4s.dsl.io._
import org.http4s.headers.Location
import org.http4s.Uri

val routes = HttpRoutes.of[IO] {
  // Permanent redirect
  case GET -> Root / "old-path" =>
    MovedPermanently(Location(Uri.unsafeFromString("/new-path")))
    
  // Temporary redirect with body
  case GET -> Root / "temp" =>
    Found(Location(Uri.unsafeFromString("/temporary-location")), "Redirecting...")
    
  // Post-redirect-get pattern
  case POST -> Root / "submit" =>
    SeeOther(Location(Uri.unsafeFromString("/success")))
    
  // Not modified (for caching)
  case GET -> Root / "resource" if notModified =>
    NotModified()
}

Client Error Responses (4xx)

Status codes indicating client-side errors with specialized response generators.

// 4xx Client Error Status Codes
val BadRequest: Status.BadRequest.type                              // 400
val Unauthorized: Status.Unauthorized.type                          // 401
val PaymentRequired: Status.PaymentRequired.type                    // 402
val Forbidden: Status.Forbidden.type                                // 403
val NotFound: Status.NotFound.type                                  // 404
val MethodNotAllowed: Status.MethodNotAllowed.type                  // 405
val NotAcceptable: Status.NotAcceptable.type                        // 406
val ProxyAuthenticationRequired: Status.ProxyAuthenticationRequired.type // 407
val RequestTimeout: Status.RequestTimeout.type                      // 408
val Conflict: Status.Conflict.type                                  // 409
val Gone: Status.Gone.type                                          // 410
val LengthRequired: Status.LengthRequired.type                      // 411
val PreconditionFailed: Status.PreconditionFailed.type              // 412
val PayloadTooLarge: Status.PayloadTooLarge.type                    // 413
val UriTooLong: Status.UriTooLong.type                              // 414
val UnsupportedMediaType: Status.UnsupportedMediaType.type          // 415
val RangeNotSatisfiable: Status.RangeNotSatisfiable.type            // 416
val ExpectationFailed: Status.ExpectationFailed.type                // 417
val MisdirectedRequest: Status.MisdirectedRequest.type              // 421
val UnprocessableEntity: Status.UnprocessableEntity.type            // 422
val Locked: Status.Locked.type                                      // 423
val FailedDependency: Status.FailedDependency.type                  // 424
val TooEarly: Status.TooEarly.type                                  // 425
val UpgradeRequired: Status.UpgradeRequired.type                    // 426
val PreconditionRequired: Status.PreconditionRequired.type          // 428
val TooManyRequests: Status.TooManyRequests.type                    // 429
val RequestHeaderFieldsTooLarge: Status.RequestHeaderFieldsTooLarge.type // 431
val UnavailableForLegalReasons: Status.UnavailableForLegalReasons.type   // 451

Usage Examples:

import org.http4s.dsl.io._
import org.http4s.headers.{`WWW-Authenticate`, Allow}

val routes = HttpRoutes.of[IO] {
  // Bad request with validation errors
  case POST -> Root / "users" =>
    // Validation logic
    if (invalidInput) {
      BadRequest("Invalid user data provided")
    } else {
      Created("User created")
    }
    
  // Unauthorized with WWW-Authenticate header
  case GET -> Root / "protected" =>
    Unauthorized(`WWW-Authenticate`(Challenge("Bearer", "api")))
    
  // Not found with helpful message
  case GET -> Root / "users" / IntVar(id) =>
    // Lookup user
    userService.findById(id).flatMap {
      case Some(user) => Ok(user.asJson)
      case None => NotFound(s"User with ID $id not found")
    }
    
  // Method not allowed with Allow header
  case req @ POST -> Root / "readonly" =>
    MethodNotAllowed(Allow(Set(GET, HEAD)))
    
  // Conflict for duplicate resources
  case POST -> Root / "users" =>
    // Check for existing user
    if (userExists) {
      Conflict("User already exists")
    } else {
      Created("User created")
    }
}

Server Error Responses (5xx)

Status codes indicating server-side errors and service unavailability.

// 5xx Server Error Status Codes
val InternalServerError: Status.InternalServerError.type              // 500
val NotImplemented: Status.NotImplemented.type                        // 501
val BadGateway: Status.BadGateway.type                                // 502
val ServiceUnavailable: Status.ServiceUnavailable.type                // 503
val GatewayTimeout: Status.GatewayTimeout.type                        // 504
val HttpVersionNotSupported: Status.HttpVersionNotSupported.type      // 505
val VariantAlsoNegotiates: Status.VariantAlsoNegotiates.type          // 506
val InsufficientStorage: Status.InsufficientStorage.type              // 507
val LoopDetected: Status.LoopDetected.type                            // 508
val NotExtended: Status.NotExtended.type                              // 510
val NetworkAuthenticationRequired: Status.NetworkAuthenticationRequired.type // 511

Usage Examples:

import org.http4s.dsl.io._

val routes = HttpRoutes.of[IO] {
  // Handle application errors
  case GET -> Root / "data" =>
    dataService.fetchData().attempt.flatMap {
      case Right(data) => Ok(data.asJson)
      case Left(error) => 
        logger.error("Data fetch failed", error)
        InternalServerError("Unable to fetch data")
    }
    
  // Service temporarily unavailable
  case GET -> Root / "maintenance" =>
    ServiceUnavailable("Service under maintenance")
    
  // Not implemented features
  case PUT -> Root / "beta-feature" =>
    NotImplemented("Feature not yet implemented")
}

Informational Responses (1xx)

Status codes for informational responses (less commonly used).

// 1xx Informational Status Codes
val Continue: Status.Continue.type                    // 100
val SwitchingProtocols: Status.SwitchingProtocols.type // 101
val Processing: Status.Processing.type                 // 102
val EarlyHints: Status.EarlyHints.type                // 103

Content Type and Encoding

Response generators automatically handle content types and encoding based on the EntityEncoder instances.

import org.http4s.dsl.io._
import org.http4s.circe._
import io.circe.generic.auto._

val routes = HttpRoutes.of[IO] {
  // JSON response (with circe EntityEncoder)
  case GET -> Root / "api" / "users" =>
    val users = List(User("Alice", 25), User("Bob", 30))
    Ok(users.asJson)  // Content-Type: application/json
    
  // Plain text response
  case GET -> Root / "health" =>
    Ok("OK")  // Content-Type: text/plain
    
  // HTML response  
  case GET -> Root =>
    Ok("<html><body><h1>Welcome</h1></body></html>")
      .map(_.withContentType(`Content-Type`(MediaType.text.html)))
}

Error Handling Best Practices

The DSL enables consistent error handling patterns:

import org.http4s.dsl.io._

val routes = HttpRoutes.of[IO] {
  case GET -> Root / "users" / IntVar(id) =>
    userService.findById(id).flatMap {
      case Some(user) => Ok(user.asJson)
      case None => NotFound(s"User $id not found")
    }.handleErrorWith { error =>
      logger.error(s"Error fetching user $id", error)
      InternalServerError("Internal server error")
    }
}

Type Safety

  • All status constants are singleton types for compile-time safety
  • Response generators preserve effect type F[_] and entity type information
  • EntityEncoder instances ensure type-safe response body serialization
  • Headers are type-safe through the http4s header system