or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdhttp-methods-routing.mdindex.mdpath-matching.mdquery-parameters.mdstatus-codes-responses.md
tile.json

status-codes-responses.mddocs/

Status Codes and Response Generation

Complete set of HTTP status codes with fluent response generation API. Each status code provides type-safe methods for creating responses with or without bodies, headers, and proper content negotiation.

Capabilities

Response Generation Pattern

All HTTP status codes follow a consistent pattern for response generation:

/**
 * Base response generator trait
 */
trait ResponseGenerator {
  def status: Status
}

/**
 * Response generator for responses without body content
 */
trait EmptyResponseGenerator[F[_], G[_]] extends 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]]
}

/**
 * Response generator for responses with optional body content
 */
trait EntityResponseGenerator[F[_], G[_]] extends ResponseGenerator {
  def liftG: G ~> F
  def apply()(implicit F: Applicative[F]): 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]]
}

1xx Informational Responses

Informational status codes for ongoing request processing.

/**
 * 100 Continue - Request headers received, client should send body
 */
val Continue: Status.Continue.type

/**
 * 101 Switching Protocols - Server switching to different protocol
 */
val SwitchingProtocols: Status.SwitchingProtocols.type

/**
 * 102 Processing - Server received request, still processing
 */
val Processing: Status.Processing.type

/**
 * 103 Early Hints - Early metadata before final response
 */
val EarlyHints: Status.EarlyHints.type

Usage Examples:

val routes = HttpRoutes.of[IO] {
  case GET -> Root / "status" =>
    Continue()
    
  case GET -> Root / "upgrade" =>
    SwitchingProtocols()
}

2xx Success Responses

Success status codes indicating successful request processing.

/**
 * 200 OK - Standard successful response
 */
val Ok: Status.Ok.type

/**
 * 201 Created - Resource successfully created
 */
val Created: Status.Created.type

/**
 * 202 Accepted - Request accepted for processing
 */
val Accepted: Status.Accepted.type

/**
 * 203 Non-Authoritative Information - Successful but from cache/proxy
 */
val NonAuthoritativeInformation: Status.NonAuthoritativeInformation.type

/**
 * 204 No Content - Successful request with no response body
 */
val NoContent: Status.NoContent.type

/**
 * 205 Reset Content - Successful request, client should reset form
 */
val ResetContent: Status.ResetContent.type

/**
 * 206 Partial Content - Partial resource delivered
 */
val PartialContent: Status.PartialContent.type

/**
 * 207 Multi-Status - Multiple status codes for WebDAV
 */
val MultiStatus: Status.MultiStatus.type

/**
 * 208 Already Reported - WebDAV already reported
 */
val AlreadyReported: Status.AlreadyReported.type

/**
 * 226 IM Used - Instance manipulations applied
 */
val IMUsed: Status.IMUsed.type

Usage Examples:

val routes = HttpRoutes.of[IO] {
  // Simple responses
  case GET -> Root / "hello" =>
    Ok("Hello, World!")
    
  case POST -> Root / "users" =>
    Created("User created successfully")
    
  // With custom headers
  case GET -> Root / "data" =>
    Ok("response body", Header.Raw(ci"X-Custom-Header", "custom-value"))
    
  // Empty responses
  case DELETE -> Root / "users" / IntVar(id) =>
    NoContent()
    
  // JSON responses
  case GET -> Root / "users" / IntVar(id) =>
    Ok(User(id, "John Doe").asJson)
}

3xx Redirection Responses

Redirection status codes requiring additional client action.

/**
 * 300 Multiple Choices - Multiple representation options
 */
val MultipleChoices: Status.MultipleChoices.type

/**
 * 301 Moved Permanently - Resource permanently moved
 */
val MovedPermanently: Status.MovedPermanently.type

/**
 * 302 Found - Resource temporarily moved
 */
val Found: Status.Found.type

/**
 * 303 See Other - Response available at different URI
 */
val SeeOther: Status.SeeOther.type

/**
 * 304 Not Modified - Resource not modified since last request
 */
val NotModified: Status.NotModified.type

/**
 * 307 Temporary Redirect - Temporary redirect preserving method
 */
val TemporaryRedirect: Status.TemporaryRedirect.type

/**
 * 308 Permanent Redirect - Permanent redirect preserving method
 */
val PermanentRedirect: Status.PermanentRedirect.type

Usage Examples:

import org.http4s.headers.Location
import org.http4s.Uri

val routes = HttpRoutes.of[IO] {
  // Redirects with Location header
  case GET -> Root / "old-path" =>
    MovedPermanently(Location(Uri.unsafeFromString("/new-path")))
    
  case POST -> Root / "login" =>
    SeeOther(Location(Uri.unsafeFromString("/dashboard")))
    
  // Conditional responses
  case req @ GET -> Root / "resource" =>
    req.headers.get[`If-Modified-Since`] match {
      case Some(since) if !isModifiedSince(since) => NotModified()
      case _ => Ok("Resource content")
    }
}

4xx Client Error Responses

Client error status codes indicating issues with client requests.

/**
 * 400 Bad Request - Invalid request syntax
 */
val BadRequest: Status.BadRequest.type

/**
 * 401 Unauthorized - Authentication required
 */
val Unauthorized: Status.Unauthorized.type

/**
 * 402 Payment Required - Payment required (reserved)
 */
val PaymentRequired: Status.PaymentRequired.type

/**
 * 403 Forbidden - Access denied
 */
val Forbidden: Status.Forbidden.type

/**
 * 404 Not Found - Resource not found
 */
val NotFound: Status.NotFound.type

/**
 * 405 Method Not Allowed - HTTP method not supported
 */
val MethodNotAllowed: Status.MethodNotAllowed.type

/**
 * 406 Not Acceptable - Response format not acceptable
 */
val NotAcceptable: Status.NotAcceptable.type

/**
 * 407 Proxy Authentication Required - Proxy authentication needed
 */
val ProxyAuthenticationRequired: Status.ProxyAuthenticationRequired.type

/**
 * 408 Request Timeout - Request timeout
 */
val RequestTimeout: Status.RequestTimeout.type

/**
 * 409 Conflict - Request conflicts with current state
 */
val Conflict: Status.Conflict.type

/**
 * 410 Gone - Resource permanently gone
 */
val Gone: Status.Gone.type

/**
 * 411 Length Required - Content-Length header required
 */
val LengthRequired: Status.LengthRequired.type

/**
 * 412 Precondition Failed - Precondition in headers failed
 */
val PreconditionFailed: Status.PreconditionFailed.type

/**
 * 413 Payload Too Large - Request entity too large
 */
val PayloadTooLarge: Status.PayloadTooLarge.type

/**
 * 414 URI Too Long - Request URI too long
 */
val UriTooLong: Status.UriTooLong.type

/**
 * 415 Unsupported Media Type - Media type not supported
 */
val UnsupportedMediaType: Status.UnsupportedMediaType.type

/**
 * 416 Range Not Satisfiable - Range header cannot be satisfied
 */
val RangeNotSatisfiable: Status.RangeNotSatisfiable.type

/**
 * 417 Expectation Failed - Expect header cannot be satisfied
 */
val ExpectationFailed: Status.ExpectationFailed.type

/**
 * 421 Misdirected Request - Request directed to wrong server
 */
val MisdirectedRequest: Status.MisdirectedRequest.type

/**
 * 422 Unprocessable Entity - Request syntactically correct but semantically invalid
 */
val UnprocessableEntity: Status.UnprocessableEntity.type

/**
 * 423 Locked - Resource locked
 */
val Locked: Status.Locked.type

/**
 * 424 Failed Dependency - Request failed due to dependency failure
 */
val FailedDependency: Status.FailedDependency.type

/**
 * 425 Too Early - Request sent too early
 */
val TooEarly: Status.TooEarly.type

/**
 * 426 Upgrade Required - Client must upgrade protocol
 */
val UpgradeRequired: Status.UpgradeRequired.type

/**
 * 428 Precondition Required - Precondition header required
 */
val PreconditionRequired: Status.PreconditionRequired.type

/**
 * 429 Too Many Requests - Rate limit exceeded
 */
val TooManyRequests: Status.TooManyRequests.type

/**
 * 431 Request Header Fields Too Large - Headers too large
 */
val RequestHeaderFieldsTooLarge: Status.RequestHeaderFieldsTooLarge.type

/**
 * 451 Unavailable For Legal Reasons - Content blocked for legal reasons
 */
val UnavailableForLegalReasons: Status.UnavailableForLegalReasons.type

Usage Examples:

val routes = HttpRoutes.of[IO] {
  // Error responses with messages
  case POST -> Root / "users" =>
    for {
      user <- req.as[User].handleErrorWith(_ => BadRequest("Invalid user data").pure)
      result <- createUser(user).handleErrorWith(_ => Conflict("User already exists").pure)
    } yield result
    
  // Authentication errors
  case GET -> Root / "protected" =>
    Unauthorized("""WWW-Authenticate: Bearer realm="api"""")
    
  // Not found
  case GET -> Root / "users" / IntVar(id) =>
    findUser(id).flatMap {
      case Some(user) => Ok(user.asJson)
      case None => NotFound(s"User $id not found")
    }
    
  // Validation errors
  case req @ POST -> Root / "validate" =>
    req.as[Data].attempt.flatMap {
      case Right(data) => Ok("Valid data")
      case Left(_) => UnprocessableEntity("Invalid data format")
    }
}

5xx Server Error Responses

Server error status codes indicating server-side issues.

/**
 * 500 Internal Server Error - Generic server error
 */
val InternalServerError: Status.InternalServerError.type

/**
 * 501 Not Implemented - Server doesn't support functionality
 */
val NotImplemented: Status.NotImplemented.type

/**
 * 502 Bad Gateway - Invalid response from upstream server
 */
val BadGateway: Status.BadGateway.type

/**
 * 503 Service Unavailable - Server temporarily unavailable
 */
val ServiceUnavailable: Status.ServiceUnavailable.type

/**
 * 504 Gateway Timeout - Upstream server timeout
 */
val GatewayTimeout: Status.GatewayTimeout.type

/**
 * 505 HTTP Version Not Supported - HTTP version not supported
 */
val HttpVersionNotSupported: Status.HttpVersionNotSupported.type

/**
 * 506 Variant Also Negotiates - Content negotiation error
 */
val VariantAlsoNegotiates: Status.VariantAlsoNegotiates.type

/**
 * 507 Insufficient Storage - Server out of storage
 */
val InsufficientStorage: Status.InsufficientStorage.type

/**
 * 508 Loop Detected - Infinite loop detected
 */
val LoopDetected: Status.LoopDetected.type

/**
 * 510 Not Extended - Further extensions required
 */
val NotExtended: Status.NotExtended.type

/**
 * 511 Network Authentication Required - Network authentication required
 */
val NetworkAuthenticationRequired: Status.NetworkAuthenticationRequired.type

Usage Examples:

val routes = HttpRoutes.of[IO] {
  case GET -> Root / "data" =>
    fetchData().handleErrorWith { error =>
      error match {
        case _: DatabaseConnectionException => ServiceUnavailable("Database temporarily unavailable")
        case _: TimeoutException => GatewayTimeout("Request timeout")
        case _ => InternalServerError("An unexpected error occurred")
      }
    }
    
  case GET -> Root / "unsupported" =>
    NotImplemented("This endpoint is not yet implemented")
}

Specialized Response Generators

Some status codes have specialized response generators with additional requirements:

WWW-Authenticate Response Generator (401)

/**
 * 401 responses must include WWW-Authenticate header
 */
trait WwwAuthenticateResponseGenerator[F[_], G[_]] extends 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]]
}

Allow Response Generator (405)

/**
 * 405 responses must include Allow header
 */
trait AllowResponseGenerator[F[_], G[_]] extends 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]]
}

Proxy-Authenticate Response Generator (407)

/**
 * 407 responses must include Proxy-Authenticate header
 */
trait ProxyAuthenticateResponseGenerator[F[_], G[_]] extends ResponseGenerator {
  def apply(authenticate: `Proxy-Authenticate`, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
  def apply[A](authenticate: `Proxy-Authenticate`, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
}

Location Response Generator (3xx)

/**
 * Redirect responses should include Location header
 */
trait LocationResponseGenerator[F[_], G[_]] extends 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]]
}