mochi/middleware

Types

Filter to determine which fields a middleware applies to

pub type FieldFilter {
  SpecificFields(List(#(String, String)))
  TypeFields(List(String))
  NamedFields(List(String))
  AllFields
}

Constructors

  • SpecificFields(List(#(String, String)))

    Apply to specific (type, field) pairs

  • TypeFields(List(String))

    Apply to all fields on specific types

  • NamedFields(List(String))

    Apply to fields with specific names (on any type)

  • AllFields

    Apply to all fields

Middleware function type Takes the current resolution state and a “next” function to continue the chain

pub type Middleware =
  fn(Resolution, fn(Resolution) -> Resolution) -> Resolution

A middleware definition with metadata

pub type MiddlewareDef {
  MiddlewareDef(
    name: String,
    priority: Int,
    field_filter: option.Option(FieldFilter),
    middleware: fn(Resolution, fn(Resolution) -> Resolution) -> Resolution,
  )
}

Constructors

  • MiddlewareDef(
      name: String,
      priority: Int,
      field_filter: option.Option(FieldFilter),
      middleware: fn(Resolution, fn(Resolution) -> Resolution) -> Resolution,
    )

    Arguments

    name

    Name for debugging/logging

    priority

    Priority - lower values run first (default: 100)

    field_filter

    Optional filter to only apply to certain fields

    middleware

    The middleware function

The middleware pipeline containing all registered middleware

pub opaque type MiddlewarePipeline

Resolution state passed through the middleware chain

pub type Resolution {
  Resolution(
    value: option.Option(dynamic.Dynamic),
    error: option.Option(String),
    field_name: String,
    parent_type: String,
    context: ResolverContext,
    private: dict.Dict(String, dynamic.Dynamic),
  )
}

Constructors

  • Resolution(
      value: option.Option(dynamic.Dynamic),
      error: option.Option(String),
      field_name: String,
      parent_type: String,
      context: ResolverContext,
      private: dict.Dict(String, dynamic.Dynamic),
    )

    Arguments

    value

    The resolved value (None if not yet resolved or if error occurred)

    error

    Error message if resolution failed

    field_name

    Field name being resolved

    parent_type

    Parent type name

    context

    Resolver context

    private

    Private data that middleware can use to communicate

Information about the current field being resolved

pub type ResolverContext {
  ResolverContext(
    field_name: String,
    parent_type: String,
    path: List(String),
    info: schema.ResolverInfo,
  )
}

Constructors

  • ResolverContext(
      field_name: String,
      parent_type: String,
      path: List(String),
      info: schema.ResolverInfo,
    )

    Arguments

    field_name

    The field name being resolved

    parent_type

    The parent type name

    path

    Current path in the query

    info

    The full resolver info

Values

pub fn add_middleware(
  pipeline: MiddlewarePipeline,
  mw: MiddlewareDef,
) -> MiddlewarePipeline

Add middleware to the pipeline

pub fn auth_middleware(
  role_extractor: fn(schema.UserContext) -> option.Option(String),
  required_role: String,
) -> MiddlewareDef

Authorization middleware that checks for a specific role in context.

role_extractor receives a decoder-friendly view of the user context. The middleware decodes via the supplied decoder, then asks the extractor for the role string.

pub fn caching_middleware(
  cache_key: fn(Resolution) -> String,
  get_cached: fn(String) -> option.Option(dynamic.Dynamic),
  set_cached: fn(String, dynamic.Dynamic) -> Nil,
) -> MiddlewareDef

Caching middleware that caches field results

pub fn error_wrapper_middleware(
  wrapper: fn(String) -> String,
) -> MiddlewareDef

Error wrapping middleware that transforms errors

pub fn execute_with_middleware(
  pipeline: MiddlewarePipeline,
  parent_type: String,
  field_def: schema.FieldDefinition,
  info: schema.ResolverInfo,
  resolver: fn(schema.ResolverInfo) -> Result(
    dynamic.Dynamic,
    String,
  ),
) -> Result(dynamic.Dynamic, String)

Execute a resolver with the middleware pipeline

pub fn get_private(
  resolution: Resolution,
  key: String,
) -> option.Option(dynamic.Dynamic)

Get private data from the resolution

pub fn has_error(resolution: Resolution) -> Bool

Check if resolution has an error

pub fn has_value(resolution: Resolution) -> Bool

Check if resolution has a value

pub fn logging_middleware(
  log_fn: fn(String) -> Nil,
) -> MiddlewareDef

Logging middleware that logs field resolution

pub fn middleware(
  name: String,
  mw_fn: fn(Resolution, fn(Resolution) -> Resolution) -> Resolution,
) -> MiddlewareDef

Create a basic middleware definition

pub fn new_pipeline() -> MiddlewarePipeline

Create a new empty middleware pipeline

pub fn new_resolution(
  field_name: String,
  parent_type: String,
  context: ResolverContext,
) -> Resolution

Create a new resolution state for a field

pub fn put_private(
  resolution: Resolution,
  key: String,
  value: dynamic.Dynamic,
) -> Resolution

Add private data to the resolution

pub fn rate_limit_middleware(
  key_extractor: fn(Resolution) -> String,
  max_requests: Int,
  checker: fn(String, Int) -> Bool,
) -> MiddlewareDef

Rate limiting middleware (simple in-memory version) Note: For production, use external state (Redis, etc.)

pub fn set_error(
  resolution: Resolution,
  error: String,
) -> Resolution

Set an error on the resolution

pub fn set_value(
  resolution: Resolution,
  value: dynamic.Dynamic,
) -> Resolution

Set the resolved value

pub fn timing_middleware(
  get_time: fn() -> Int,
  record_timing: fn(String, String, Int) -> Nil,
) -> MiddlewareDef

Timing middleware that records field resolution time

pub fn to_executor_fn(
  pipeline: MiddlewarePipeline,
) -> fn(
  String,
  schema.FieldDefinition,
  schema.ResolverInfo,
  fn(schema.ResolverInfo) -> Result(dynamic.Dynamic, String),
) -> Result(dynamic.Dynamic, String)

Convert a MiddlewarePipeline to a schema.MiddlewareFn

This is used to wire a middleware pipeline into the execution context without creating a circular dependency between schema and middleware.

Example

let pipeline = middleware.new_pipeline()
  |> middleware.add_middleware(middleware.logging_middleware(io.println))

let ctx = schema.execution_context(user_ctx)
  |> schema.with_middleware(middleware.to_executor_fn(pipeline))
pub fn transform_middleware(
  transform: fn(dynamic.Dynamic) -> dynamic.Dynamic,
) -> MiddlewareDef

Transform middleware that transforms the resolved value

pub fn validation_middleware(
  validator: fn(schema.ResolverInfo) -> Result(Nil, String),
) -> MiddlewareDef

Validation middleware that validates arguments

pub fn with_filter(
  mw: MiddlewareDef,
  filter: FieldFilter,
) -> MiddlewareDef

Set a field filter on middleware

pub fn with_priority(
  mw: MiddlewareDef,
  priority: Int,
) -> MiddlewareDef

Set the priority of a middleware (lower runs first)

Search Document