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) {
  case validation.validate_json(request.body, user_decoder) {
    Ok(user) -> 
      // Valid user data, proceed with business logic
      create_user(services.db, user)
    
    Error(err) -> 
      // Validation failed, return error 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/validation
import dream/http/response
import dream/http/status
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 validation.validate_json(request.body, post_decoder) {
    Ok(post) -> {
      // Valid post data - proceed with business logic
      let created = insert_post(services.db, post)
      response.json_response(status.created, post_to_json(created))
    }
    
    Error(err) -> {
      // Validation failed - return error response
      let error_json = json.object([
        #("error", json.string(err.message)),
        #("field", case err.field {
          Some(f) -> json.string(f)
          None -> json.null()
        })
      ])
      response.json_response(status.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