dream/http/error

Dream unified error type

A single error type used throughout Dream applications for both HTTP-level validation errors and domain-level operation errors. This eliminates the need for error conversion between layers.

Philosophy

Instead of having separate error types for HTTP validation, database operations, and business logic, Dream uses one unified Error type. This means you can return errors from any layer without conversion, and the type system tracks them naturally.

Usage

Return errors from controllers, models, or operations using the same type:

import dream/http/error.{type Error, BadRequest, NotFound}
import gleam/result

pub fn get_user(db: Connection, id: Int) -> Result(User, Error) {
  query_user(db, id)
  |> result.replace_error(NotFound("User not found"))
}

pub fn create_user(db: Connection, name: String) -> Result(User, Error) {
  case validate_name(name) {
    True -> insert_user(db, name)
    False -> Error(BadRequest("Name must not be empty"))
  }
}

Converting to Responses

Use to_status_code() and message() to convert errors to HTTP responses:

import dream/http/error.{type Error, message, to_status_code}
import dream/http/response.{type Response, json_response}
import gleam/json

let result = get_user(db, id)

case result {
  Ok(user) -> json_response(200, user_to_json(user))
  Error(err) -> create_error_response(err)
}

fn create_error_response(err: Error) -> Response {
  let error_json = json.object([#("error", json.string(message(err)))])
  json_response(to_status_code(err), error_json)
}

Types

Unified error type for Dream applications

Maps semantic error types to HTTP status codes. Each variant includes a message string for context-specific error details.

Variants

  • BadRequest: Client sent invalid data (malformed, missing required fields)
  • Unauthorized: Authentication is required but not provided
  • Forbidden: Authenticated but lacks permission for this action
  • NotFound: Requested resource doesn’t exist
  • UnprocessableContent: Valid syntax but fails business logic validation
  • InternalServerError: Unexpected server error (database, external API, etc.)
pub type Error {
  BadRequest(String)
  Unauthorized(String)
  Forbidden(String)
  NotFound(String)
  UnprocessableContent(String)
  InternalServerError(String)
}

Constructors

  • BadRequest(String)

    400 Bad Request - Invalid or missing parameter, validation failure

    Use when client sends malformed data, missing required fields, or syntactically invalid input (e.g., “abc” when expecting integer).

    Example

    Error(BadRequest("Field 'email' is required"))
    Error(BadRequest("ID must be a positive integer"))
    
  • Unauthorized(String)

    401 Unauthorized - Authentication required

    Use when request requires valid authentication credentials. Despite the name, this means “not authenticated” (no valid credentials).

    Example

    Error(Unauthorized("Authentication token required"))
    Error(Unauthorized("Invalid or expired token"))
    
  • Forbidden(String)

    403 Forbidden - Authenticated but not authorized

    Use when user is authenticated but lacks permission for this action. Unlike Unauthorized, providing credentials won’t help.

    Example

    Error(Forbidden("Admin access required"))
    Error(Forbidden("Cannot delete another user's post"))
    
  • NotFound(String)

    404 Not Found - Resource doesn’t exist

    Use when requested resource doesn’t exist or user shouldn’t know it exists (to hide existence instead of returning 403).

    Example

    Error(NotFound("User not found"))
    Error(NotFound("Post with ID 123 does not exist"))
    
  • UnprocessableContent(String)

    422 Unprocessable Content - Semantic validation failed

    Use when request is syntactically valid but fails business logic validation (e.g., date in past, email already taken, invalid state).

    Example

    Error(UnprocessableContent("Email already registered"))
    Error(UnprocessableContent("Start date must be before end date"))
    
  • InternalServerError(String)

    500 Internal Server Error - Unexpected error (database, external service, etc)

    Use for unexpected server errors: database failures, external API errors, filesystem errors, etc. Should be logged for investigation.

    Example

    Error(InternalServerError("Database connection failed"))
    Error(InternalServerError("External payment API unavailable"))
    

Values

pub fn message(error: Error) -> String

Extract the error message from an Error

Returns the message string from any error variant. Useful when building error responses or logging.

Example

import dream/http/error.{type Error, BadRequest}
import gleam/json

let err = BadRequest("Invalid email format")
let msg = error.message(err)  // Returns "Invalid email format"

// Build JSON error response
json.object([
  #("error", json.string(msg)),
  #("status", json.int(error.to_status_code(err)))
])
pub fn to_status_code(error: Error) -> Int

Convert Error to HTTP status code

Maps each error variant to its corresponding HTTP status code. Use this when building HTTP responses from errors.

Example

import dream/http/error.{type Error, NotFound}
import dream/http/response

let err = NotFound("User not found")
let status = error.to_status_code(err)  // Returns 404
response.json_response(status, error_json(err))
Search Document