dream/http/validation

JSON validation with structured error messages

Validate and decode JSON request bodies with detailed error information. Validation is kept separate from response building - controllers decide how to handle validation errors.

Quick Example

import dream/http/validation
import gleam/dynamic/decode

pub type CreateUser {
  CreateUser(name: String, email: String, age: Int)
}

let user_decoder = {
  use name <- decode.field("name", decode.string)
  use email <- decode.field("email", decode.string)
  use age <- decode.field("age", decode.int)
  decode.success(CreateUser(name, email, age))
}

pub fn create(request, context, services) {
  let validation_result = validation.validate_json(request.body, user_decoder)
  
  case validation_result {
    Ok(user) -> create_user(services.db, user)
    Error(err) -> create_validation_error_response(err)
  }
}

fn create_validation_error_response(err: ValidationError) -> Response {
  response.json_response(status.bad_request, error_json(err.message))
}

Error Handling

ValidationError provides detailed information about what went wrong:

Types

Validation error with field-level details

Provides detailed information about JSON validation failures, including the specific field that failed and what was expected vs. found.

Fields

  • message: Complete error message suitable for logging or user display
  • field: The JSON path to the field that failed (e.g., “user.email”)
  • expected: The expected type (e.g., “String”, “Int”)
  • found: The actual type found (e.g., “Null”, “Bool”)

Example

import dream/http/validation.{ValidationError}
import gleam/option

// Example error when expecting string but got number
ValidationError(
  message: "Expected String but found Int at user.email",
  field: option.Some("user.email"),
  expected: option.Some("String"),
  found: option.Some("Int")
)
pub type ValidationError {
  ValidationError(
    message: String,
    field: option.Option(String),
    expected: option.Option(String),
    found: option.Option(String),
  )
}

Constructors

Values

pub fn validate_json(
  body: String,
  decoder: decode.Decoder(decoded_type),
) -> Result(decoded_type, ValidationError)

Validate and decode JSON request body

Parses JSON string and decodes it using the provided decoder. Returns the decoded data or a detailed validation error.

This function is generic over the return type - use gleam/dynamic/decode to define decoders for your types.

Example

import dream/http/response.{json_response}
import dream/http/status.{bad_request, created}
import dream/http/validation.{validate_json}
import gleam/dynamic/decode
import gleam/json

pub type CreatePost {
  CreatePost(title: String, body: String, published: Bool)
}

let post_decoder = {
  use title <- decode.field("title", decode.string)
  use body <- decode.field("body", decode.string)
  use published <- decode.field("published", decode.bool)
  decode.success(CreatePost(title, body, published))
}

pub fn create_post(request, context, services) {
  case validate_json(request.body, post_decoder) {
    Ok(post) -> {
      // Valid post data - proceed with business logic
      let created = insert_post(services.db, post)
      json_response(created, post_to_json(created))
    }
    
    Error(err) -> {
      let field_json = format_field_as_json(err.field)
      let error_json = json.object([
        #("error", json.string(err.message)),
        #("field", field_json)
      ])
      json_response(bad_request, json.to_string(error_json))
    }
  }
}

Error Cases

  • Invalid JSON syntax: Returns error with parse details
  • Missing required field: Returns error with field path
  • Wrong type: Returns error with expected vs. found types
  • Invalid enum value: Returns error with validation details
Search Document