Sign in

    This version of the module is deprecated: Moved to moonbitstack/moonapi.

    moonapi

    moonapi — a typed web framework for MoonBit (← FastAPI): routing, typed extractors, descriptor-driven validation, multi-version OpenAPI/Swagger (2.0 / 3.0 / 3.1) with security schemes, dependency injection, OAuth2 password-bearer with self-built HS256 JWT and scopes, per-operation security enforcement, multipart/urlencoded form and file extractors, response_model filtering, background tasks, sub-application mounting, a CORS/gzip(real DEFLATE)/exception-handler middleware stack with per-status handlers, Server-Sent Events, and WebSocket routes, on the moonasgi SEAM.

    fastapi
    web
    framework
    openapi
    swagger
    asgi
    moonbit
    Download zip
    Author
    Version
    0.7.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    609

    #moonapi

    A typed web framework for MoonBit — ← FastAPI.

    Check and Test License mooncakes

    moonapi builds an AsgiApp from typed routes and generates its own OpenAPI / Swagger document — the role FastAPI plays for Python. It depends only on moonasgi, so it's backend-agnostic (routing and OpenAPI run in-process on every backend); a server such as mooncat runs the resulting app.

    flowchart LR routes["typed routes<br/>App::get / post / …"] --> app["**moonapi** App"] app -->|"App::to_asgi()"| asgi(["moonasgi AsgiApp"]) app -->|"App::openapi()"| spec["OpenAPI 2.0 / 3.0 / 3.1"] asgi --> cat["mooncat serves it"]

    #Quickstart

    let app = @moonapi.App::new()
    app.get("/", _ctx => @moonapi.text(200, "Hello from moonapi!"))
    app.get("/users/:id", ctx => @moonapi.text(200, "user " + ctx.param("id").unwrap()),
    summary="fetch a user")
    app.post("/users", _ctx => @moonapi.text(201, "created"))

    // One set of routes → every mainstream spec version:
    let v31 = app.openapi_json(version=OpenApi31) // OpenAPI 3.1.0
    let v30 = app.openapi_json(version=OpenApi30) // OpenAPI 3.0.3
    let v20 = app.openapi_json(version=Swagger20) // Swagger 2.0
    let docs_page = @moonapi.swagger_ui() // a Swagger UI page

    // Serve it (native, via mooncat):
    @mooncat.serve(app.to_asgi(), port=8000)

    #What's here (v0)

    • Routing — App::get/post/put/patch/delete/route, :param path segments extracted into Context::param, correct 404 (no path) vs 405 (path but not method).
    • Descriptor tree — a runtime Schema / Param / Endpoint tree (a one-first-class-value substitute for FastAPI's from-signature reflection) that a typed route carries. Walked once to (a) emit complete OpenAPI request/response body schemas — objects, arrays, scalars, required, with named models hoisted under components/schemas and referenced by $ref — and (b) drive request validation off the same tree. User structs describe themselves with derive(ToJson) + a T::schema() associated function + a one-line ToSchema bridge (the mctl-friendly shape).
    • Multi-version OpenAPI — App::openapi / openapi_json emit Swagger 2.0, OpenAPI 3.0.3, and OpenAPI 3.1.0 from the same routes and descriptors (3.x requestBody + components/schemas; 2.0 body-parameter + definitions), because a good FastAPI is not pinned to one spec version.
    • Typed body extractors — Context::body[T] deserialises the JSON body into a derive(FromJson) struct; Context::body_validated[T] first checks it against the endpoint descriptor and returns either the built value or a FastAPI-shaped 422 error list — schema emission, validation, and deserialisation all off one descriptor.
    • Dependency injection — a Container (provider registry + dependency_overrides) with request-scoped, one-shot resolution (per-request caching) and yield-style teardown run LIFO around the handler — the explicit MoonBit equivalent of FastAPI's Depends.
    • OAuth2 + JWT security — a /token password-grant endpoint issues an HS256 JWT (create_access_token), and an OAuth2PasswordBearer reads the Authorization: Bearer header, verifies the token, and enforces scopes: 401 on a missing or invalid/expired token, 403 when a valid token lacks a required scope. The SHA-256 / HMAC-SHA256 pair is self-built (crypto.mbt), checked against the NIST and RFC 4231 vectors; the alg: "none" downgrade is refused and signatures compare in constant time.
    • Form & file extractors — Context::form parses both an application/x-www-form-urlencoded body (percent- and +-decoded) and a multipart/form-data body, splitting the boundary stream into FormFields and byte-exact UploadFiles (filename + content-type + raw bytes). Context::oauth2_password_form reads the OAuth2 password form off it.
    • response_model — filter_response / json_model validate a handler's return value against a declared Schema and project it down to exactly the model's fields, so a route can hold a richer object internally than it exposes (an id, a password hash) and still emit only what it promised.
    • Middleware & exception handlers — an outer middleware chain (App::middleware) with cors(...) (preflight + actual-request headers, configurable origins/methods/headers, credentials, exposed headers) and gzip(...) (a real DEFLATE compressor, below), plus exception handlers (App::exception_handler): a handler raises an HttpException and the app maps it to a response, falling through to a built-in {"detail": ...} for HttpException and a 500 for anything else. App::add_status_handler(code, ...) swaps in a custom response for any error status — a routing 404 / 405 or a raised exception's status — so an app can serve its own error pages.
    • Per-operation security — a route carries security=[SecurityRequirement::new(scheme, scopes=[...])]. App::secure_oauth2(name, bearer) both surfaces the scheme in the spec and registers it as a guard, so the app pulls and verifies the bearer token and checks the route's scopes before the handler — 401 unauthenticated, 403 on a missing scope — and emits the requirement as the operation's OpenAPI security array. A bare add_security_scheme documents without enforcing (FastAPI's split between a scheme and a wired dependency).
    • Background tasks — a background-aware route (App::route_bg) receives a BackgroundTasks queue; add_task defers work that the app runs, in order, after the response is sent (← FastAPI's BackgroundTasks), so a slow write never delays the client.
    • Sub-application mounting — App::mount(prefix, subapp) composes routers: a request under prefix is routed by the sub-app with the prefix stripped (its own middleware, security, and background tasks apply), and the sub-app's routes and security schemes fold into the parent's merged OpenAPI document under the prefix. Mounts nest.
    • Server-Sent Events — ServerSentEvent frames per the WHATWG event-stream format (id / event / retry / multi-line data / : comments) and sse_response builds the text/event-stream envelope.
    • WebSocket routes — App::websocket(path, handler) over the moonasgi WS SEAM. The handler drives a WebSocket (accept / receive / send / close); it's a synchronous core, so drive_websocket runs it against an in-memory frame queue in a test and App::to_asgi serves it over the async transport.
    • Security schemes — App::add_security_scheme surfaces the OAuth2 / JWT layer in the emitted spec (components/securitySchemes in 3.x, securityDefinitions in 2.0); OAuth2PasswordBearer::scheme builds the password-flow object.
    • Swagger UI — swagger_ui() returns a ready-to-serve documentation page.
    • Responses — text and json helpers over moonasgi.Response.

    Verified across all backends (wasm, wasm-gc, js, native) in CI, 0 warnings under --deny-warn.

    #Design notes

    Two places make an explicit, documented trade-off rather than a silent shortcut:

    • GZip produces a valid RFC 1952 gzip stream — correct header, CRC-32, and ISIZE — around a real RFC 1951 DEFLATE payload: LZ77 back-reference matching (a 32 KiB window, hash-chain match finder) coded with the fixed Huffman table (deflate.mbt), so the body actually shrinks. A companion inflate decodes it, so the encoder is round-trip-verified on every backend, and the system gzip reads the output in CI. The one increment left is dynamic Huffman (a per-block code fit to the data) for a tighter ratio; fixed Huffman already delivers a genuine ratio (roughly 19× on a repetitive body).
    • WebSocket handlers are a synchronous core: the same handler runs in a test and under a server. Because the core can't suspend on the async transport (MoonBit runs async only in an async context), the serving shell buffers the client's inbound frames, runs the handler, then emits its frames. Content and order are preserved — exact for echo, broadcast, and request-reply — but it doesn't interleave live per-frame with the client.

    #Roadmap (transliterating FastAPI)

    The descriptor tree (Endpoint / Param / Schema) drives OpenAPI body schemas and validation; on top sit the typed derive(FromJson) body extractors, the dependency-injection container, the OAuth2 password-bearer security layer with self-built HS256 JWT, the Form / File extractors, and response_model filtering. On the middleware side: CORS, a real DEFLATE gzip, exception and per-status handlers, Server-Sent Events, and WebSocket routes. This release wires per-operation security from the declared schemes (enforced, and emitted into the spec), background tasks that run after the response, and sub-application mounting with a merged OpenAPI document. Next: RS256 / ES256 signing (a self-built RSA/ECDSA bignum stack); dynamic-Huffman DEFLATE for a tighter ratio; static files and templates; and codegen'd request schemas via moonctl.

    #License

    Apache-2.0.

    ApiHandler

    type ApiHandler = (Context) ->
    Response
    raise

    A moonapi route handler: request context in, response out. It may raise an HttpException (or any error) instead of returning — the app catches it and maps it to a response through the registered exception handlers, so handlers read like FastAPI's raise HTTPException(...) rather than threading a Result back by hand. A plain non-raising closure is still a valid handler.

    BackgroundHandler

    type BackgroundHandler = (Context, BackgroundTasks) ->
    Response
    raise

    A background-aware handler: it additionally receives the request's BackgroundTasks queue, so it can schedule work to run after its response is sent (← declaring a BackgroundTasks parameter in FastAPI).

    ExceptionHandler

    type ExceptionHandler = (Context, Error) ->
    Response
    ?

    An exception handler: given the request context and the raised error, return Some(response) to handle it or None to defer to the next handler. The explicit MoonBit form of FastAPI's @app.exception_handler(ExcType) — the None case stands in for "this handler isn't registered for that type".

    WsHandler

    type WsHandler = (WebSocket) -> Unit

    A WebSocket route handler: given the connection, drive the exchange. Usually accept, then a receive loop, then close.

    ToSchema

    pub(open) trait ToSchema {
    fn to_schema(Self) -> Schema
    }

    The impl-able version of a type's descriptor. A user struct implements it — the value is ignored; it exists so descriptor-carrying code can be generic over "a type that knows its own schema". The primary, mctl-friendly shape is still a plain associated function T::schema() -> Schema (no instance needed, mirroring FastAPI referencing the model class); this trait bridges to it.

    HttpException

    pub(all) suberror HttpException {
    HttpException(status~ : Int, detail~ : Json, headers~ : Array[(String, String)])
    }

    An HTTP error a handler can raise to short-circuit with a status and body (← FastAPI's HTTPException). detail is any JSON (a string is the common case); headers are added to the response (e.g. a WWW-Authenticate challenge). Caught by the app and mapped to a response.

    JwtError

    pub suberror JwtError {
    MalformedToken(String)
    UnsupportedAlg(String)
    BadSignature
    Expired
    NotYetValid
    }

    A JWT verification failure. Each way a token can be rejected is reported distinctly so the Security layer can map it to the right status and a caller can log precisely which check failed.

    Address

    pub(all) struct Address {
    city : String
    zip : String
    } derive(Eq, ToJson)

    A nested value type, to show a $ref chain: User.address references this under components/schemas.
    impl ToSchema for Address

    Address::schema

    fn Address::schema() -> Schema

    The Address descriptor.

    ApiInfo

    pub(all) struct ApiInfo {
    title : String
    api_version : String
    description : String
    terms_of_service : String
    contact : Contact?
    license : License?
    servers : Array[Server]
    }

    What the document says about the API itself — FastAPI's info block plus servers. An app carries one (App::describe sets it) so that every emission of the document, including the route enable_docs registers, agrees.

    ApiInfo::new

    fn ApiInfo::new() -> ApiInfo

    The defaults an app starts with.

    App

    pub struct App {
    routes : Array[Route]
    ws_routes : Array[WsRoute]
    middlewares : Array[((
    Request
    ) ->
    Response
    ) -> ((
    Request
    ) ->
    Response
    )]
    exception_handlers : Array[(Context, Error) ->
    Response
    ?]
    security_schemes : Array[(String, SecurityScheme)]
    enforcers : Map[String, (Context, Array[String], Int64) -> Result[AuthenticatedUser,
    Response
    ]]
    status_handlers : Map[Int, (Context) ->
    Response
    ]
    mounts : Array[(String, App)]
    startup_hooks : Array[() -> Unit raise]
    shutdown_hooks : Array[() -> Unit raise]
    info : ApiInfo
    clock : () -> Int64
    }

    A moonapi application: routes, WebSocket routes, an outer middleware chain, exception handlers, mounted sub-applications, the security schemes surfaced in the OpenAPI document (with optional runtime enforcers), per-status exception handlers, and the verification clock. Compiles to a moonasgi AsgiApp any server (mooncat) can run.

    App::add_security_scheme

    fn App::add_security_scheme(self : App, name : String, scheme : SecurityScheme) -> Unit

    Declare a security scheme under name, surfaced in the emitted OpenAPI document (components/securitySchemes in 3.x, securityDefinitions in Swagger 2.0) so the generated spec describes how to authenticate.

    App::add_status_handler

    fn App::add_status_handler(self : App, status : Int, handler : (Context) ->
    Response
    ) -> Unit

    Register a per-status exception handler (← FastAPI's add_exception_handler keyed by an HTTP status code). Whenever an error path yields status — a routing 404 / 405, or a raised HttpException (or the fallback 500) — the handler's response replaces the default, so an app can serve a custom error page. Successful handler returns are never rewritten.

    App::delete

    fn App::delete(self : App, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a DELETE route.

    App::describe

    fn App::describe(self : App, title? : String, api_version? : String, description? : String, terms_of_service? : String, contact? : Contact?, license? : License?, servers? : Array[Server]) -> Unit

    Set what the OpenAPI document says about this API (← FastAPI's FastAPI(title=…,description=…, contact=…, license_info=…, servers=…)). App::openapi and the /openapi.json route enable_docs registers both read it, so the document a client fetches and the one a test builds cannot describe different APIs.

    App::enable_docs

    fn App::enable_docs(self : App, openapi_url? : String?, docs_url? : String?, redoc_url? : String?, version? : OpenApiVersion) -> Unit

    Serve the app's own documentation, the way FastAPI does out of the box: the OpenAPI document at openapi_url, Swagger UI at docs_url, and ReDoc at redoc_url. Pass None for any of the three to leave it off. The three routes are kept out of the document they serve, so enabling docs does not change the spec a client reads.

    It is a call rather than a default because registering routes behind the app's back would surprise anyone mounting this app under a prefix.

    App::exception_handler

    fn App::exception_handler(self : App, h : (Context, Error) ->
    Response
    ?) -> Unit

    Register an exception handler. On a raised error the handlers are tried in registration order; the first to return Some(response) wins. An unhandled error falls through to the built-in mapping — an HttpException becomes its own status / detail, anything else a 500.

    App::get

    fn App::get(self : App, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a GET route. A shorthand for route(Get, ...) carrying the same documentation and security arguments.

    App::handle

    Route a request to its handler through the middleware chain, returning 404 when no path matches and 405 when a path matches but no method does. Any error a handler raises is mapped to a response by the exception handlers. Background tasks a handler scheduled are dropped on this path; use handle_with_background (as to_asgi does) to run them.

    App::handle_with_background

    Route a request through the middleware chain and return both the response and the background queue the handler filled — the caller (to_asgi, or a test) runs the queue after the response is sent. handle is the plain-response wrapper over this.

    App::lifespan_handler

    This app's hooks as a moonasgi LifespanHandler — the synchronous core, which is what makes boot and teardown testable on every backend without a server. Mounted apps are included: a mount is part of the composition being started, and nothing else would ever drive its hooks.

    App::middleware

    Add an outer middleware. Middlewares wrap the router as an onion; the first registered is the outermost (it sees the request first and the response last). cors(...) and gzip(...) are middlewares.

    App::mount

    fn App::mount(self : App, prefix : String, subapp : App) -> Unit

    Mount a sub-application under prefix (← FastAPI's app.mount(prefix, sub)). A request whose path lies under prefix is routed by subapp with the prefix stripped (its own middleware, security, and background tasks apply), and the sub-app's routes appear under prefix in the merged OpenAPI document with its security schemes folded into the parent's.

    App::new

    fn App::new() -> App

    Create an empty application. The verification clock defaults to 0 (Unix epoch); a server sets a real one with App::set_clock, and tests inject a fixed time so token expiry is deterministic.

    App::on_shutdown

    fn App::on_shutdown(self : App, hook : () -> Unit raise) -> Unit

    Run hook when the server shuts down, after the last request has been served (← FastAPI's on_event("shutdown")). Hooks run in reverse registration order, so a resource is released before whatever it was opened from. A hook that raises does not stop the others — shutdown reports the first failure once the rest have run.

    App::on_startup

    fn App::on_startup(self : App, hook : () -> Unit raise) -> Unit

    Run hook when the server starts, before it accepts the first request (← FastAPI's on_event("startup")). Hooks run in the order they were registered; one that raises aborts startup, and the server is told why.

    App::openapi

    fn App::openapi(self : App, version? : OpenApiVersion, title? : String, api_version? : String, description? : String, terms_of_service? : String, contact? : Contact?, license? : License?, servers? : Array[Server]) -> Json

    Build the app's OpenAPI document as a Json value, walking the registered routes — this app's and every mounted sub-app's, each under its prefix — into paths, methods and operations. version picks the dialect: Swagger 2.0, OpenAPI 3.0.3 or 3.1.0 off the identical routes. Routes registered with include_in_schema=false are left out.

    App::openapi_json

    fn App::openapi_json(self : App, version? : OpenApiVersion) -> String

    The app's OpenAPI / Swagger document for version, stringified.

    App::patch

    fn App::patch(self : App, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a PATCH route: change part of the addressed resource.

    App::post

    fn App::post(self : App, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a POST route — the verb that carries a request body, so this is the one most often given an endpoint describing that body.

    App::put

    fn App::put(self : App, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a PUT route: replace the addressed resource wholesale.

    App::route

    fn App::route(self : App, verb : Method, path : String, handler : (Context) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a route for an explicit method. An optional endpoint descriptor makes the route fully typed (its parameters, request body, and responses surface in the OpenAPI document and drive validation); security attaches per-operation requirements (emitted as OpenAPI security and enforced before the handler when their scheme has an enforcer).

    App::route_bg

    fn App::route_bg(self : App, verb : Method, path : String, handler : (Context, BackgroundTasks) ->
    Response
    raise, summary? : String, tags? : Array[String], deprecated? : Bool, endpoint? : Endpoint?, security? : Array[SecurityRequirement], include_in_schema? : Bool, validate? : Bool) -> Unit

    Register a background-aware route: the handler additionally receives the request's BackgroundTasks queue, whose thunks the app runs after the response is sent (← declaring a BackgroundTasks parameter in FastAPI).

    App::secure_oauth2

    fn App::secure_oauth2(self : App, name : String, bearer : OAuth2PasswordBearer, scopes? : Array[(String, String)]) -> Unit

    Declare an OAuth2 password-bearer scheme under name and wire it as a runtime enforcer. Like add_security_scheme it surfaces the scheme in the OpenAPI document (with the advertised scopes), and additionally registers the guard a route names in its security: the app pulls the bearer token, verifies it against bearer's secret at the app clock's time, and checks the route's required scopes — returning 401/403 before the handler runs.

    App::set_clock

    fn App::set_clock(self : App, clock : () -> Int64) -> Unit

    Set the clock the app reads to verify token expiry when enforcing route security (Unix seconds). A native server passes the wall clock; a test passes a fixed function so expiry is deterministic.

    App::to_asgi

    fn App::to_asgi(self : App) -> (async (
    Scope
    , async () ->
    Event
    , async (
    Event
    ) -> Unit) -> Unit)

    Compile the app to a moonasgi AsgiApp a server can run: drain the request body, route it, and stream the response back over the SEAM.

    App::websocket

    fn App::websocket(self : App, path : String, handler : (WebSocket) -> Unit) -> Unit

    Register a WebSocket route (← FastAPI's @app.websocket(path)). The path matches with the same :name segment rules as HTTP routes.

    AuthenticatedUser

    pub(all) struct AuthenticatedUser {
    subject : String
    scopes : Array[String]
    claims : Map[String, Json]
    }

    The identity a verified token carries, injected into a protected handler (← the object FastAPI's get_current_user returns). subject is the sub claim, scopes the granted scopes, and claims the whole verified payload for anything else the handler needs (exp, custom claims).

    AuthenticatedUser::has_scope

    fn AuthenticatedUser::has_scope(self : AuthenticatedUser, scope : String) -> Bool

    Whether this user was granted scope.

    BackgroundTasks

    pub struct BackgroundTasks {
    tasks : Array[() -> Unit]
    }

    A queue of deferred thunks (← FastAPI's BackgroundTasks). A background-aware route receives one per request, calls add_task to enqueue work, and the app runs the queue — in enqueue order — after the response has been sent.

    BackgroundTasks::add_task

    fn BackgroundTasks::add_task(self : BackgroundTasks, task : () -> Unit) -> Unit

    Enqueue a thunk to run after the response is sent. Tasks run in the order they were added, each after the previous returns (← BackgroundTasks.add_task).

    BackgroundTasks::len

    fn BackgroundTasks::len(self : BackgroundTasks) -> Int

    How many tasks are queued — the app checks this to skip the drain when a route scheduled nothing.

    BackgroundTasks::new

    An empty task queue.

    BackgroundTasks::run

    fn BackgroundTasks::run(self : BackgroundTasks) -> Unit

    Run every queued task in order, then clear the queue. Called by the app once the response has been handed to the transport, so a task's latency never delays the client. Idempotent: a second call runs nothing.

    Constraint

    pub(all) enum Constraint {
    Minimum(Double)
    Maximum(Double)
    ExclusiveMinimum(Double)
    ExclusiveMaximum(Double)
    MultipleOf(Double)
    MinLength(Int)
    MaxLength(Int)
    Pattern(String)
    MinItems(Int)
    MaxItems(Int)
    } derive(Eq)

    A value constraint on a field (JSON-Schema keyword ↔ Pydantic argument).

    Contact

    pub(all) struct Contact {
    name : String
    url : String
    email : String
    }

    Build the OpenAPI / Swagger document for the app as a Json value, walking the registered routes once into paths → methods → operations. OpenAPI contact info (← FastAPI's contact): every field optional, emitted only when non-empty.

    Container

    pub(all) struct Container[V] {
    providers : Map[String, Provider[V]]
    overrides : Map[String, Provider[V]]
    }

    The provider registry: key -> Provider, plus a separate overrides map that shadows it. Overrides are FastAPI's app.dependency_overrides — a test swaps a real dependency (a live DB session) for a fake without touching the routes. A registered override always wins over the base provider.

    Container::clear_override

    fn[V] Container::clear_override(self : Container[V], key : String) -> Unit

    Drop the override for key (no-op if none), restoring the base provider.

    Container::clear_overrides

    fn[V] Container::clear_overrides(self : Container[V]) -> Unit

    Drop every override — the usual test teardown that returns the container to its production wiring.

    Container::new

    fn[V] Container::new() -> Container[V]

    An empty container.

    Container::open_scope

    fn[V] Container::open_scope(self : Container[V]) -> Scope[V]

    Open a fresh request scope over this container.

    Container::override_

    fn[V] Container::override_(self : Container[V], key : String, factory : () -> V, teardown? : (V) -> Unit) -> Container[V]

    Register a dependency override for key — FastAPI's app.dependency_overrides[dep] = fake. Takes precedence over the base provider until cleared.

    Container::provide

    fn[V] Container::provide(self : Container[V], key : String, factory : () -> V, teardown? : (V) -> Unit) -> Container[V]

    Register a base provider under key (last registration wins), returning the container so registrations can chain.

    Container::provide_using

    fn[V] Container::provide_using(self : Container[V], key : String, factory : (Scope[V]) -> V, teardown? : (V) -> Unit) -> Container[V]

    Register a base provider whose factory resolves sub-dependencies through the request scope it is handed (FastAPI's nested Depends). Otherwise like provide.

    Container::run

    Run handler inside a fresh request scope, then tear the scope down — the setup/teardown pair wrapped around a handler, exactly as a FastAPI yield dependency brackets the request. The handler resolves whatever it needs through the scope; every dependency built during the call is released (LIFO) once it returns, then the response is handed back.

    Context

    pub(all) struct Context {
    request :
    Request

    params : Map[String, String]
    }

    The per-request context handed to a handler: the raw request plus the path parameters extracted from the matched route (:name segments).
    fn Context::api_key_cookie(self : Context, name : String) -> String?

    The API key carried in the cookie name (← FastAPI's APIKeyCookie), or None.

    Context::api_key_header

    fn Context::api_key_header(self : Context, name : String) -> String?

    The API key carried in the request header name (← FastAPI's APIKeyHeader), or None. Header names are matched against the request's lower-cased headers.

    Context::api_key_query

    fn Context::api_key_query(self : Context, name : String) -> String?

    The API key carried in the query parameter name (← FastAPI's APIKeyQuery), or None.

    Context::bearer_token

    fn Context::bearer_token(self : Context) -> String?

    Pull the bearer token out of the Authorization: Bearer <token> header, None if the header is absent or isn't a bearer credential. The scheme name is matched case-insensitively, as RFC 6750 requires.

    Context::body

    Deserialise the JSON request body into a user type T, None when the body is absent, is not valid JSON, or does not shape-match T. This is the unchecked, best-effort extractor — the total counterpart of writing item: Item on a FastAPI handler when you don't want the framework's 422. T describes itself with derive(@json.FromJson); the deserialisation is core's, so it stays faithful to the JSON shape without any reflection.

    Context::body_json

    fn Context::body_json(self : Context) -> Json?

    Parse the request body as JSON, returning None for an empty body or one that does not parse — the total counterpart of FastAPI reading a JSON body.

    Context::body_validated

    fn[T :
    FromJson
    ] Context::body_validated(self : Context, schema : Schema) -> Result[T, Array[ValidationError]]

    The validated typed-body extractor — the faithful equivalent of FastAPI declaring a pydantic model parameter: the body is checked against the endpoint's descriptor schema (the same tree that emits the OpenAPI body schema), and only if it conforms is it deserialised into T. On failure it yields the FastAPI-shaped ValidationError list (located under ["body",...]), ready for unprocessable; on success it yields the built T.

    Reusing validate_schema here is the point of the descriptor tree: schema emission, request validation, and typed deserialisation are all driven off one source of truth, exactly as pydantic derives all three from one model. Because validation runs first, @json.from_json is reached only for a shape-conforming value; the final catch keeps the extractor total for the residual cases a scalar schema cannot express (e.g. an out-of-range integer).

    Context::cookie

    fn Context::cookie(self : Context, name : String) -> String?

    Look up a cookie by name from the request Cookie header, which is a ; -separated list of key=value pairs. Surrounding spaces are trimmed; None if there is no Cookie header or the name is absent.

    Context::form

    fn Context::form(self : Context) -> FormData

    The parsed request form — FastAPI's Form(...) / File(...) parameters. Dispatches on the Content-Type: a multipart/form-data body is split on its boundary into fields and files, an application/x-www-form-urlencoded body is decoded into fields. Any other (or absent) content type yields an empty form rather than raising, so the extractor stays total.

    Context::http_basic

    fn Context::http_basic(self : Context) -> HttpBasicCredentials?

    The HTTP Basic credentials on this request (← FastAPI's HTTPBasic dependency), or None.

    Context::json_field

    fn Context::json_field(self : Context, name : String) -> Json?

    Pull a single field out of a JSON object body by name, None if the body is absent, not an object, or lacks the field.

    Context::oauth2_password_form

    fn Context::oauth2_password_form(self : Context) -> OAuth2PasswordRequestForm?

    Read an OAuth2PasswordRequestForm off the request's urlencoded (or multipart) body. None when neither username nor password is present — the body isn't a password-grant form at all.

    Context::param

    fn Context::param(self : Context, name : String) -> String?

    Look up a path parameter by name.

    Context::query

    fn Context::query(self : Context, name : String) -> String?

    Look up a query-string parameter by name, e.g. ?limit=10&q=cat%20dog. Keys and values are percent/plus-decoded, so q above reads back as cat dog. When a key repeats, the first occurrence wins; use query_all to read every one.

    Context::query_all

    fn Context::query_all(self : Context, name : String) -> Array[String]

    Every value given for name, in the order they appear — ?tag=a&tag=b reads back as ["a", "b"]. Empty when the key is absent.

    CorsConfig

    pub(all) struct CorsConfig {
    allow_origins : Array[String]
    allow_all_origins : Bool
    allow_methods : Array[String]
    allow_headers : Array[String]
    allow_all_headers : Bool
    allow_credentials : Bool
    expose_headers : Array[String]
    max_age : Int
    }

    CORS policy (← Starlette's CORSMiddleware). Origins, methods, and headers are allow-lists; the *_all flags open a dimension wholesale. Per the Fetch standard, allow_credentials forbids the * wildcard in the reflected Access-Control-Allow-Origin, so with credentials the request origin is echoed back instead.

    Dep

    pub(all) enum Dep {
    Greeting(String)
    } derive(Eq)

    The dependency value type of the greet app. A sum type wrapping every dependency this app injects — the explicit, exhaustive stand-in for FastAPI resolving heterogeneous Depends values dynamically.

    EcdsaPrivateKey

    pub(all) struct EcdsaPrivateKey {
    d :
    BigInt

    }

    An ECDSA P-256 private key: the scalar d. The ES256 signing key.

    EcdsaPrivateKey::from_hex

    fn EcdsaPrivateKey::from_hex(d_hex : String) -> EcdsaPrivateKey

    Build a P-256 private key from its hex-encoded scalar.

    EcdsaPrivateKey::public_key

    The public key Q = d·G for this private key.

    EcdsaPublicKey

    An ECDSA P-256 public key: the curve point (x, y). The ES256 verification key.

    EcdsaPublicKey::from_hex

    fn EcdsaPublicKey::from_hex(x_hex : String, y_hex : String) -> EcdsaPublicKey

    Build a P-256 public key from the hex-encoded affine coordinates — e.g. the two halves of openssl's uncompressed pub point after its 04 prefix.

    Ed25519PrivateKey

    pub(all) struct Ed25519PrivateKey {
    seed : Bytes
    }

    An Ed25519 (EdDSA) private key: the 32-byte secret seed. The signing key for the JWT EdDSA algorithm (RFC 8037).

    Ed25519PrivateKey::from_hex

    fn Ed25519PrivateKey::from_hex(hex : String) -> Ed25519PrivateKey

    Build an Ed25519 private key from its 32-byte seed in hex.

    Ed25519PrivateKey::public_key

    The public key matching this private key: A = [s]B.

    Ed25519PublicKey

    pub(all) struct Ed25519PublicKey {
    key : Bytes
    }

    An Ed25519 (EdDSA) public key: the 32-byte compressed point. The verification key for the JWT EdDSA algorithm (RFC 8037).

    Ed25519PublicKey::from_hex

    fn Ed25519PublicKey::from_hex(hex : String) -> Ed25519PublicKey

    Build an Ed25519 public key from its 32-byte hex encoding.

    Endpoint

    pub(all) struct Endpoint {
    params : Array[Param]
    request_body : Schema?
    request_required : Bool
    responses : Array[ResponseSpec]
    } derive(Eq)

    The runtime endpoint descriptor a route can carry — the one first-class value that replaces FastAPI reading a handler's signature. Walked once for the OpenAPI operation (parameters + requestBody + responses, with every named object hoisted into components/schemas) and for request validation.

    Endpoint::new

    fn Endpoint::new(params? : Array[Param], request_body? : Schema?, request_required? : Bool, responses? : Array[ResponseSpec]) -> Endpoint

    Build an endpoint descriptor. Everything is optional: a bare Endpoint::new() describes an endpoint with no parameters, no body, and (on emit) a default 200 OK response.

    Endpoint::validate

    fn Endpoint::validate(self : Endpoint, ctx : Context) -> Array[ValidationError]

    Validate an inbound request ctx against this endpoint descriptor: every declared parameter (path / query / header / cookie) plus the JSON request body, all off the same descriptor tree that emits the OpenAPI operation. Returns the accumulated errors — an empty array means the request conforms, otherwise pass them to unprocessable for a FastAPI-shaped 422.

    Field

    pub(all) struct Field {
    name : String
    schema : Schema
    required : Bool
    description : String
    constraints : Array[Constraint]
    } derive(Eq)

    One field of an object schema: its name, its schema, whether it is required, and an optional description.

    Field::new

    fn Field::new(name : String, schema : Schema, required? : Bool, description? : String, constraints? : Array[Constraint]) -> Field

    Build a field. required defaults to true (FastAPI treats a field without a default as required). constraints are the Pydantic-style value constraints (ge/le/min_length/pattern/…) that are both emitted into the field's OpenAPI schema and enforced when validating an inbound value.

    FormData

    pub(all) struct FormData {
    fields : Array[FormField]
    files : Array[UploadFile]
    } derive(Eq)

    A parsed form: its plain fields and its uploaded files, in body order. A multipart part is a file when its Content-Disposition carries a filename; otherwise it's a field. An urlencoded body only ever yields fields.

    FormData::field

    fn FormData::field(self : FormData, name : String) -> String?

    The value of the first field named name, None if absent — the common Form(...) lookup.

    FormData::field_all

    fn FormData::field_all(self : FormData, name : String) -> Array[String]

    Every value submitted under name, in order — an HTML form can repeat a field (checkbox groups, multi-selects), and FastAPI surfaces those as a list.

    FormData::file

    fn FormData::file(self : FormData, name : String) -> UploadFile?

    The first uploaded file under name, None if absent — the File(...) lookup.

    FormField

    pub(all) struct FormField {
    name : String
    value : String
    } derive(Eq)

    A plain form field: a name and its decoded text value.

    GreetReq

    pub(all) struct GreetReq {
    name : String
    } derive(Eq, ToJson,
    FromJson
    )

    The request body of POST /greet. derive(@json.FromJson) lets Context::body_validated build it after the descriptor accepts the payload; its schema and struct fields agree (both require name), so a schema-valid body always deserialises.

    GreetReq::schema

    fn GreetReq::schema() -> Schema

    The GreetReq descriptor — one required string field.

    HttpBasicCredentials

    pub(all) struct HttpBasicCredentials {
    username : String
    password : String
    }

    The credentials carried in an HTTP Basic Authorization header (← FastAPI's HTTPBasicCredentials): the username and password from base64(username:password).

    License

    pub(all) struct License {
    name : String
    url : String
    }

    OpenAPI license info (← FastAPI's license_info): name required, url optional.

    Method

    pub(all) enum Method {
    Get
    Post
    Put
    Patch
    Delete
    Head
    Options
    } derive(Eq)

    HTTP methods a moonapi route can bind to.

    NewUser

    pub(all) struct NewUser {
    name : String
    email : String
    age : Int
    } derive(Eq, ToJson)

    The demo request model — the body of POST /users. age is optional.
    impl ToSchema for NewUser

    NewUser::schema

    fn NewUser::schema() -> Schema

    The NewUser descriptor.

    OAuth2PasswordBearer

    pub(all) struct OAuth2PasswordBearer {
    token_url : String
    secret : String
    }

    An OAuth2 password-bearer security scheme (← FastAPI's OAuth2PasswordBearer). Holds the token_url (the endpoint that issues tokens, surfaced to API docs) and the shared HS256 secret used to verify presented tokens.

    OAuth2PasswordBearer::authenticate

    fn OAuth2PasswordBearer::authenticate(self : OAuth2PasswordBearer, ctx : Context, now_secs : Int64, scopes? : Array[String]) -> Result[AuthenticatedUser,
    Response
    ]

    Authenticate a request against this scheme and enforce scopes (← FastAPI's Security(get_current_user, scopes=[...])). On success returns the AuthenticatedUser; otherwise the response to return:

    • no bearer token -> 401 {"detail":"Not authenticated"}
    • malformed / bad-signature / expired / not-yet-valid token -> 401 {"detail":"Could not validate credentials"}
    • valid token missing a required scope -> 403 {"detail":"Not enough permissions"}

    now_secs is the verification time (Unix seconds), passed in so the check stays pure and testable on every backend.

    OAuth2PasswordBearer::new

    fn OAuth2PasswordBearer::new(token_url : String, secret : String) -> OAuth2PasswordBearer

    Build a password-bearer scheme pointing at the token endpoint at token_url, verifying tokens with secret.

    OAuth2PasswordBearer::scheme

    fn OAuth2PasswordBearer::scheme(self : OAuth2PasswordBearer, scopes? : Array[(String, String)]) -> SecurityScheme

    Build the security scheme that describes this password-bearer flow (← the object FastAPI derives from OAuth2PasswordBearer). scopes are the (name, description) pairs advertised in the OpenAPI document.

    OAuth2PasswordRequestForm

    pub(all) struct OAuth2PasswordRequestForm {
    grant_type : String
    username : String
    password : String
    scopes : Array[String]
    client_id : String?
    client_secret : String?
    } derive(Eq)

    The parsed OAuth2 password-grant form (← FastAPI's OAuth2PasswordRequestForm). The token endpoint reads username / password to authenticate and scopes (the space-delimited scope field, split into a list) to stamp into the issued token. grant_type is "password" for this flow; client_id / client_secret are optional confidential-client credentials.

    ObjectSchema

    pub(all) struct ObjectSchema {
    name : String
    fields : Array[Field]
    description : String
    } derive(Eq)

    The body of an object schema: its component name (empty = an anonymous inline object; non-empty = hoisted to components/schemas and referenced), its ordered fields, and an optional description.

    OpenApiVersion

    pub(all) enum OpenApiVersion {
    Swagger20
    OpenApi30
    OpenApi31
    } derive(Eq)

    Target OpenAPI / Swagger document version. moonapi emits every mainstream version from the same route descriptors — a "good FastAPI" is not pinned to one spec version.

    Param

    pub(all) struct Param {
    name : String
    loc : ParamLoc
    schema : Schema
    required : Bool
    description : String
    } derive(Eq)

    A single request parameter descriptor: its name, location, scalar schema, whether it is required, and an optional description. Path parameters are always required (OpenAPI requires it); the constructor keeps the caller's value but validation treats path params as mandatory.

    Param::new

    fn Param::new(name : String, loc : ParamLoc, schema? : Schema, required? : Bool, description? : String) -> Param

    Build a parameter descriptor. schema defaults to a string and required to true.

    ParamLoc

    pub(all) enum ParamLoc {
    InPath
    InQuery
    InHeader
    InCookie
    } derive(Eq)

    Where a parameter is carried, the OpenAPI in locations: the path, the query string, a header, or a cookie.

    Provider

    pub(all) struct Provider[V] {
    factory : (Scope[V]) -> V
    teardown : (V) -> Unit
    }

    A provider: a keyed factory that builds a request-scoped dependency value, with an optional teardown run after the handler (FastAPI's yield dependencies, whose post-yield body is cleanup). The factory runs at most once per request scope; the teardown receives the produced value. The factory is handed the Scope so it can resolve sub-dependencies through it — FastAPI's Depends(a) where a itself declares Depends(b).

    Provider::new

    fn[V] Provider::new(factory : () -> V, teardown? : (V) -> Unit) -> Provider[V]

    Build a leaf provider whose factory needs nothing else. teardown defaults to a no-op — the common "plain value, nothing to release" case.

    Provider::scoped

    fn[V] Provider::scoped(factory : (Scope[V]) -> V, teardown? : (V) -> Unit) -> Provider[V]

    Build a provider whose factory resolves other dependencies through the request Scope it is handed — the sub-dependency case (FastAPI's nested Depends).

    ResponseSpec

    pub(all) struct ResponseSpec {
    status : Int
    description : String
    body : Schema?
    } derive(Eq)

    A single response descriptor: the HTTP status, a human description, and an optional body schema (None for an empty body, e.g. 204).

    ResponseSpec::new

    fn ResponseSpec::new(status : Int, description? : String, body? : Schema?) -> ResponseSpec

    Build a response descriptor. description defaults to "OK" and there is no body unless one is given.

    Route

    type Route

    A route stores one background-aware handler; a plain ApiHandler is wrapped to ignore the queue. security is the per-operation requirements (emitted as OpenAPI security and enforced before the handler when their scheme has an enforcer).

    RsaPrivateKey

    An RSA private key: modulus n, public exponent e, and private exponent d (the CRT parameters are not needed for the plain m^d mod n path). The RS256 signing key.

    RsaPrivateKey::from_hex

    fn RsaPrivateKey::from_hex(n_hex : String, e_hex : String, d_hex : String) -> RsaPrivateKey

    Build a private key from hex-encoded modulus, public exponent, and private exponent.

    RsaPrivateKey::public_key

    fn RsaPrivateKey::public_key(self : RsaPrivateKey) -> RsaPublicKey

    The public half of a private key — for verifying what it signs.

    RsaPublicKey

    An RSA public key: modulus n and public exponent e (RFC 8017). The RS256 verification key. Core's BigInt carries the modular arithmetic, so the signature scheme is a straight transcription of PKCS#1 v1.5 with no vendored C.

    RsaPublicKey::from_hex

    fn RsaPublicKey::from_hex(n_hex : String, e_hex : String) -> RsaPublicKey

    Build a public key from hex-encoded modulus and exponent — e.g. openssl's rsa -modulus output and 10001.

    Schema

    pub(all) enum Schema {
    SStr
    SInt
    SFloat
    SBool
    SNull
    SArray(Schema)
    SObject(ObjectSchema)
    SEnum(Schema, Array[Json])
    } derive(Eq)

    A JSON-Schema type descriptor — the runtime, first-class value that stands in for FastAPI's from-signature reflection. One Schema tree is walked once to (a) emit a complete OpenAPI request/response body schema — objects, arrays and scalars, with required, and named objects hoisted under components/schemas and referenced by $ref — and (b) drive validation of an inbound JSON value. This is the explicit, MoonBit-idiomatic equivalent of pydantic's type-driven magic (cf. Rust serde + macros, Go struct tags + codegen). A named object carries its fields inline, so the same tree is fully self-describing for validation; the name is used only to deduplicate it into components on emit.

    Schema::array

    fn Schema::array(item : Schema) -> Schema

    An array schema whose elements all conform to item.

    Schema::object

    fn Schema::object(name : String, fields : Array[Field]) -> Schema

    A named object schema: hoisted to components/schemas under name and referenced by $ref wherever it is used.

    Scope

    pub struct Scope[V] {
    container : Container[V]
    cache : Map[String, V]
    building : Map[String, Bool]
    teardowns : Array[() -> Unit]
    }

    A request-scoped resolution scope. Each dependency is built at most once and its value cached for the life of the scope (FastAPI's per-request dependency cache), and each built value's teardown is recorded to run — in reverse registration order (LIFO) — when the scope closes. Open one per request, resolve dependencies through it, then close it (or use Container::run).

    Scope::close

    fn[V] Scope::close(self : Scope[V]) -> Unit

    Run every recorded teardown in LIFO order and clear them, so a closed scope is inert. Mirrors FastAPI unwinding yield dependencies in reverse — the last opened is torn down first.

    Scope::get

    fn[V] Scope::get(self : Scope[V], key : String) -> V?

    Resolve key within this scope: return the already-built instance if the dependency was resolved earlier in the same request; otherwise run its factory once, cache the value, register its teardown, and return it. None when no provider (or override) is registered for key.

    SecurityRequirement

    pub(all) struct SecurityRequirement {
    scheme : String
    scopes : Array[String]
    } derive(Eq)

    A security requirement on a route: the scheme name (which must match a name declared with App::add_security_scheme / App::secure_oauth2) and the scopes the caller must hold. Emitted as one {scheme: [scopes]} entry of the operation's OpenAPI security array.

    SecurityRequirement::new

    fn SecurityRequirement::new(scheme : String, scopes? : Array[String]) -> SecurityRequirement

    Require scheme with the given scopes (default: none — authentication with no scope check). App::get(..., security=[SecurityRequirement::new("OAuth2",scopes=["items"])]) reads like FastAPI's Security(oauth2, scopes=["items"]).

    SecurityScheme

    pub(all) enum SecurityScheme {
    OAuth2Password(token_url~ : String, scopes~ : Array[(String, String)])
    HttpBearer(bearer_format~ : String)
    ApiKeyHeader(name~ : String)
    ApiKeyQuery(name~ : String)
    ApiKeyCookie(name~ : String)
    HttpBasic
    }

    A security scheme describing how a client authenticates. Mirrors the OpenAPI scheme types: an OAuth2 password flow (its token URL and named scopes), an HTTP bearer scheme (with a bearerFormat such as JWT), an API key in a header / query / cookie, and HTTP Basic.

    Server

    pub(all) struct Server {
    url : String
    description : String
    }

    A servers entry (← FastAPI's servers): a base URL and an optional description.

    ServerSentEvent

    pub(all) struct ServerSentEvent {
    data : String
    event : String?
    id : String?
    retry : Int?
    comment : String?
    }

    One Server-Sent Event. data is the payload (multi-line data is split into several data: lines per the spec); event names the type an EventSource.addEventListener binds to; id sets lastEventId for reconnection; retry is the client's reconnection delay in milliseconds; comment emits :-prefixed lines (a keep-alive ping carries only this).

    ServerSentEvent::data

    fn ServerSentEvent::data(data : String) -> ServerSentEvent

    A data-only event — the common case (data: ...\n\n).

    ServerSentEvent::encode

    fn ServerSentEvent::encode(self : ServerSentEvent) -> String

    Encode this event as its wire frame: optional comment / id / event / retry fields, then one data: line per line of data, terminated by the blank line that dispatches the event.

    ServerSentEvent::keep_alive

    fn ServerSentEvent::keep_alive(comment? : String) -> ServerSentEvent

    A comment-only keep-alive frame (: <text>\n\n) — no event is dispatched, but the bytes keep the connection warm through proxies.

    ServerSentEvent::new

    fn ServerSentEvent::new(data : String, event? : String?, id? : String?, retry? : Int?, comment? : String?) -> ServerSentEvent

    A fully-specified event. Any field left None is omitted from the frame.

    UploadFile

    pub(all) struct UploadFile {
    name : String
    filename : String
    content_type : String
    content : Bytes
    } derive(Eq)

    An uploaded file from a multipart part: the form-field name it came under, the client's filename, its declared content_type (empty when the part carried no Content-Type), and the raw content bytes exactly as received.

    User

    pub(all) struct User {
    id : Int
    name : String
    address : Address
    tags : Array[String]
    } derive(Eq, ToJson)

    The demo response model — returned by both user routes. Nests Address and carries an array of tags, so its emitted schema exercises objects, $refs, and arrays together.
    impl ToSchema for User

    User::schema

    fn User::schema() -> Schema

    The User descriptor.

    ValidationError

    pub(all) struct ValidationError {
    loc : Array[String]
    msg : String
    kind : String
    }

    One entry in a 422 response's detail array, mirroring FastAPI / pydantic v2: where the error is (loc, e.g. ["query", "q"]), a human msg, and a machine kind (serialised as the JSON key type).

    ValidationError::missing

    fn ValidationError::missing(loc : Array[String]) -> ValidationError

    The canonical "a required parameter was not supplied" error located at loc, matching FastAPI's {"type": "missing", "msg": "Field required"}.

    ValidationError::type_error

    fn ValidationError::type_error(loc : Array[String], kind : String, msg : String) -> ValidationError

    A type/parse error located at loc, e.g. kind = "int_parsing" with the matching pydantic message — the shape FastAPI reports for a value of the wrong type.

    WebSocket

    pub struct WebSocket {
    inbox : Array[WsMessage]
    cursor : Int
    params : Map[String, String]
    subprotocols : Array[String]
    accepted : Bool
    closed : Bool
    outbox : Array[
    Event
    ]
    }

    The handler's view of a WebSocket connection. It reads client frames off an inbound queue and records its own actions (accept / send / close) into an outbound event log the transport replays. params are the matched :name path segments, as with an HTTP Context.

    WebSocket::accept

    fn WebSocket::accept(self : WebSocket, subprotocol? : String?, headers? : Array[(String, String)]) -> Unit

    Accept the handshake (← await websocket.accept()), optionally selecting a subprotocol and adding response headers. Idempotent: a second call is a no-op, so accept-once handlers stay simple.

    WebSocket::close

    fn WebSocket::close(self : WebSocket, code? : Int, reason? : String) -> Unit

    Close the connection with a status code (default 1000, normal closure) and reason. Idempotent.

    WebSocket::offered_subprotocols

    fn WebSocket::offered_subprotocols(self : WebSocket) -> Array[String]

    The subprotocols the client offered (the Sec-WebSocket-Protocol list).

    WebSocket::param

    fn WebSocket::param(self : WebSocket, name : String) -> String?

    Look up a matched path parameter by name.

    WebSocket::receive

    fn WebSocket::receive(self : WebSocket) -> WsMessage?

    Pull the next client frame, None once the client has sent them all (the disconnect). The receive a handler loops on.

    WebSocket::receive_bytes

    fn WebSocket::receive_bytes(self : WebSocket) -> Bytes?

    The next client frame as bytes: Some(b) for a binary frame, None on a text frame or the disconnect.

    WebSocket::receive_text

    fn WebSocket::receive_text(self : WebSocket) -> String?

    The next client frame as text: Some(s) for a text frame, None on a binary frame or the disconnect (← await websocket.receive_text()).

    WebSocket::send_bytes

    fn WebSocket::send_bytes(self : WebSocket, bytes : Bytes) -> Unit

    Send a binary frame to the client.

    WebSocket::send_text

    fn WebSocket::send_text(self : WebSocket, text : String) -> Unit

    Send a text frame to the client (← await websocket.send_text(...)).

    WebSocket::sent

    The events the handler emitted, in order — the transcript a test asserts on.

    WsMessage

    pub(all) enum WsMessage {
    WsText(String)
    WsBinary(Bytes)
    } derive(Eq)

    One inbound WebSocket message: a text frame or a binary frame.

    WsRoute

    type WsRoute

    base64url_decode

    fn base64url_decode(s : String) -> Bytes

    Decode a base64url string (padding optional) back to bytes, remapping -/_ to +// first. Lenient about missing padding, the way JWT segments are written.

    base64url_encode

    fn base64url_encode(data : Bytes) -> String

    base64url encoding (RFC 4648 §5, no padding): standard base64 with +// remapped to -/_ and trailing = dropped — the alphabet JWT uses for its header, payload, and signature segments.

    check_constraints

    fn check_constraints(value : Json, constraints : Array[Constraint], loc : Array[String], errs : Array[ValidationError]) -> Unit

    Enforce the constraints on an inbound value, appending a Pydantic-shaped ValidationError (located at loc) for each violation. A constraint that does not apply to the value's kind (a length bound on a number, say) is simply skipped, as pydantic does.

    constant_time_eq

    fn constant_time_eq(a : Bytes, b : Bytes) -> Bool

    A constant-time byte-string equality: it inspects every byte of both inputs regardless of where they first differ, so an attacker can't recover a valid signature byte-by-byte from response timing. Unequal lengths return false at once (length isn't secret). Used to compare JWT signatures.

    cors

    fn cors(allow_origins? : Array[String], allow_all_origins? : Bool, allow_methods? : Array[String], allow_headers? : Array[String], allow_all_headers? : Bool, allow_credentials? : Bool, expose_headers? : Array[String], max_age? : Int) -> (((
    Request
    ) ->
    Response
    ) -> ((
    Request
    ) ->
    Response
    ))

    A CORS middleware for the given policy. It answers preflight OPTIONS requests (those carrying Access-Control-Request-Method) directly with a 204 and the negotiated Access-Control-* headers, and decorates every other cross-origin response with Access-Control-Allow-Origin (plus Vary: Origin, exposed headers, and the credentials flag). A request with no Origin, or one from a disallowed origin, passes through untouched.

    create_access_token

    fn create_access_token(subject : String, secret : String, now_secs : Int64, scopes? : Array[String], expires_in_secs? : Int64, extra? : Map[String, Json]) -> String

    Mint an HS256 access token for subject (← FastAPI's create_access_token). Stamps sub, iat (= now_secs), exp (= now_secs + expires_in_secs), and, when non-empty, scopes; extra merges in any further claims. Times are Unix seconds. secret is the shared HS256 key.

    demo_app

    fn demo_app() -> App

    A demo application exercising the descriptor tree end to end: POST /users takes a typed NewUser body and returns a User; GET /users/:id takes a typed integer path param and returns a User. Both validate off their descriptor and both surface fully-typed bodies (with components/schemas $refs) in openapi.json.

    drive_websocket

    fn drive_websocket(handler : (WebSocket) -> Unit, inbound : Array[WsMessage], params? : Map[String, String], subprotocols? : Array[String]) -> Array[
    Event
    ]

    Run a WebSocket handler against an in-memory frame queue and return the events it emitted — the synchronous test driver (the WS half of a TestClient). Feed the client's frames as inbound; get back the handler's accept / send / close sequence.

    ecdsa_p256_sha256_sign

    fn ecdsa_p256_sha256_sign(msg : Bytes, key : EcdsaPrivateKey) -> Bytes

    ECDSA P-256 sign with SHA-256 — the ES256 signing primitive (FIPS 186-4 §6.4.1) with the deterministic nonce of RFC 6979. Returns the raw r || s (two 32-byte big-endian integers), the encoding JWT uses. Deterministic, so the same message and key always produce the same signature.

    ecdsa_p256_sha256_verify

    fn ecdsa_p256_sha256_verify(msg : Bytes, sig : Bytes, key : EcdsaPublicKey) -> Bool

    ECDSA P-256 verify with SHA-256 — the ES256 primitive (FIPS 186-4 §6.4.2). sig is the raw r || s (two 32-byte big-endian integers), the encoding JWT uses (not ASN.1 DER). Returns whether the signature is valid for msg under key: r, s in range, then u1·G + u2·Q has x-coordinate ≡ r (mod n).

    ed25519_public_from_seed

    fn ed25519_public_from_seed(seed : Bytes) -> Bytes

    Derive the 32-byte compressed public key A = [s]B from a 32-byte Ed25519 seed.

    ed25519_sign

    fn ed25519_sign(seed : Bytes, msg : Bytes) -> Bytes

    Ed25519 signing (RFC 8032 §5.1.6), deterministic. seed is the 32-byte secret key. Returns the 64-byte R || S signature: r = SHA-512(prefix || M)mod l, R = [r]B, k = SHA-512(R || A || M) mod l, S = (r + k·s) mod l.

    ed25519_verify

    fn ed25519_verify(pub_key : Bytes, msg : Bytes, sig : Bytes) -> Bool

    Ed25519 signature verification (RFC 8032 §5.1.7). sig is the 64-byte R || S, pub_key the 32-byte compressed public point. Checks [S]B = R +[k]A with k = SHA-512(R || A || M) mod l.

    filter_response

    fn filter_response(model : Schema, value : Json) -> Result[Json, Array[ValidationError]]

    Validate value against model and, if it conforms, return it filtered down to the model's declared fields (extras dropped, nested objects and arrays projected too). On a mismatch return the located ValidationErrors under ["response"] — the same shape request validation produces. This is FastAPI's response_model: the outgoing shape is the model, not whatever the handler happened to build.

    greet_app

    fn greet_app(container : Container[Dep]) -> App

    Build the greet application over a caller-supplied dependency container, so a test can register dependency_overrides on the same container before or between requests. POST /greet resolves the "greeting" dependency, reads a validated GreetReq body, and answers {"message": "<greeting>, <name>"}; a malformed body gets a FastAPI-shaped 422. The dependency scope brackets each request, so any yield teardown runs once the handler returns.

    gzip

    A GZip middleware: responses at least min_size bytes are re-encoded as gzip when the client sent Accept-Encoding: gzip and the response isn't already content-encoded. Sets Content-Encoding: gzip, updates Content-Length, and adds Vary: Accept-Encoding.

    The gzip stream is a complete RFC 1952 container — correct header, CRC-32, and ISIZE — around a real RFC 1951 DEFLATE payload: LZ77 back-references coded with the fixed Huffman table (deflate.mbt), so the body actually shrinks. Dynamic Huffman would tighten the ratio further and is the documented next step.

    hmac_sha256

    fn hmac_sha256(key : Bytes, msg : Bytes) -> Bytes

    HMAC-SHA256 (RFC 2104): a keyed MAC over sha256. A key longer than the 64-byte block is hashed first; a shorter key is zero-padded. The message is authenticated as H((K ⊕ opad) ∥ H((K ⊕ ipad) ∥ msg)). Checked against RFC 4231 test case 2. This is the signature function behind JWT HS256.

    html

    fn html(status : Int, body : String) ->
    Response

    An HTML response — what the documentation pages are served as.

    http_error

    fn http_error(status : Int, detail : String, headers? : Array[(String, String)]) -> HttpException

    Build an HttpException with a string detail — the common case — and optional extra headers. raise http_error(404, "Item not found") reads like FastAPI's raise HTTPException(404, "Item not found").

    inflate

    fn inflate(data : Bytes) -> Bytes

    Decode a raw DEFLATE stream (stored and fixed-Huffman blocks). Used to round-trip deflate_encode in the tests; back-references copy byte-by-byte so overlapping (run-length) matches inflate correctly.

    json

    fn json(status : Int, value : Json) ->
    Response

    A JSON response serialised from a Json value.

    json_model

    fn json_model(status : Int, model : Schema, value : Json) ->
    Response

    A JSON response whose body is value filtered through model. On success a status response carrying only the model's declared fields; on a model mismatch a 500 whose body lists the response-validation errors — the return value didn't match what the route promised, which is a server-side fault.

    jwt_sign

    fn jwt_sign(claims : Map[String, Json], secret : String) -> String

    Sign a claims set as a compact JWT using HS256. The header is fixed to {"alg":"HS256","typ":"JWT"}; claims is serialised as the JSON payload (exp / iat / nbf / sub / scopes go in as ordinary entries); secret is the shared HS256 key. Returns header.payload.signature, each segment base64url-encoded.

    jwt_sign_eddsa

    fn jwt_sign_eddsa(claims : Map[String, Json], key : Ed25519PrivateKey) -> String

    Sign a claims set as a compact JWT using EdDSA (Ed25519, RFC 8037). The header is fixed to {"alg":"EdDSA","typ":"JWT"}; key is the Ed25519 private key. Returns header.payload.signature, each segment base64url-encoded.

    jwt_sign_es256

    fn jwt_sign_es256(claims : Map[String, Json], key : EcdsaPrivateKey) -> String

    Sign a claims set as a compact JWT using ES256 (ECDSA P-256 / SHA-256 with the deterministic RFC 6979 nonce). The header is fixed to {"alg":"ES256","typ":"JWT"}; key is the P-256 private key. Returns header.payload.signature, each segment base64url-encoded.

    jwt_sign_rs256

    fn jwt_sign_rs256(claims : Map[String, Json], key : RsaPrivateKey) -> String

    Sign a claims set as a compact JWT using RS256 (RSASSA-PKCS1-v1_5 + SHA-256). The header is fixed to {"alg":"RS256","typ":"JWT"}; key is the RSA private key. Returns header.payload.signature, each segment base64url-encoded.

    jwt_verify

    fn jwt_verify(token : String, secret : String, now_secs : Int64) -> Map[String, Json] raise JwtError

    Verify a compact HS256 JWT and return its claims. Checks, in order: three segments; header alg is HS256; the HMAC-SHA256 signature matches (compared in constant time); exp (if present) is strictly after now_secs; nbf (if present) is at or before now_secs. now_secs is the verification time as a Unix timestamp in seconds (JWT NumericDate). Raises the matching JwtError on any failure; a tampered payload or signature fails at BadSignature.

    jwt_verify_eddsa

    fn jwt_verify_eddsa(token : String, key : Ed25519PublicKey, now_secs : Int64) -> Map[String, Json] raise JwtError

    Verify a compact EdDSA (Ed25519) JWT and return its claims (RFC 8037). Checks three segments; the header alg is EdDSA; the Ed25519 signature verifies against key; and the exp / nbf time claims. Raises the matching JwtError; a tampered payload or signature fails at BadSignature.

    jwt_verify_es256

    fn jwt_verify_es256(token : String, key : EcdsaPublicKey, now_secs : Int64) -> Map[String, Json] raise JwtError

    Verify a compact ES256 JWT and return its claims. Checks three segments; the header alg is ES256; the ECDSA-P256 / SHA-256 signature (raw r || s, the JWS encoding) verifies against key; and the exp / nbf time claims. Raises the matching JwtError; a tampered payload or signature fails at BadSignature.

    jwt_verify_rs256

    fn jwt_verify_rs256(token : String, key : RsaPublicKey, now_secs : Int64) -> Map[String, Json] raise JwtError

    Verify a compact RS256 JWT and return its claims. Checks three segments; the header alg is RS256; the RSASSA-PKCS1-v1_5 signature verifies against key; and the exp / nbf time claims. Raises the matching JwtError; a tampered payload or signature fails at BadSignature.

    oauth2_app

    fn oauth2_app(now : () -> Int64, secret? : String) -> App

    Build the OAuth2 demo application. secret is the shared HS256 key; now supplies the current Unix time (seconds) for both issuing and verifying, so a caller controls time in tests. Tokens live for one hour.

    parse_basic_auth

    fn parse_basic_auth(header : String) -> HttpBasicCredentials?

    Parse an HTTP Basic Authorization header value into credentials (← FastAPI's HTTPBasic), or None. Matches the Basic scheme case-insensitively (RFC 7617), base64-decodes the rest, and splits on the first colon so a password may itself contain colons.

    redoc_ui

    fn redoc_ui(spec_url? : String, title? : String) -> String

    A self-contained ReDoc page rendering the document served at spec_url — the second reading of the same spec FastAPI serves at /redoc, three-panel and built for reading rather than for trying calls out.

    rsa_pkcs1_sha256_sign

    fn rsa_pkcs1_sha256_sign(msg : Bytes, key : RsaPrivateKey) -> Bytes

    RSASSA-PKCS1-v1_5 sign with SHA-256 — the RS256 signing primitive (RFC 8017 §8.2.1): s = EM^d mod n, returned as a fixed-width big-endian octet string.

    rsa_pkcs1_sha256_verify

    fn rsa_pkcs1_sha256_verify(msg : Bytes, sig : Bytes, key : RsaPublicKey) -> Bool

    RSASSA-PKCS1-v1_5 verify with SHA-256 — the RS256 verification primitive (RFC 8017 §8.2.2): recover m = s^e mod n and compare it, in constant time, to the expected EMSA-PKCS1-v1_5 encoding of msg. Rejects a signature that is not the modulus width or is >= n.

    schema_of

    fn[T : ToSchema] schema_of(x : T) -> Schema

    The schema of any ToSchema type, without needing a value of it materialised at the call site beyond the one handed in — the generic entry point.

    sha256

    fn sha256(msg : Bytes) -> Bytes

    SHA-256 (FIPS 180-4): hash an arbitrary byte string to a 32-byte digest. The full message schedule and 64-round compression over 512-bit blocks with the standard length-padding, checked against the NIST vectors for "" and "abc". The building block for hmac_sha256, and through it JWT HS256.

    sha512

    fn sha512(msg : Bytes) -> Bytes

    SHA-512 (FIPS 180-4). Core ships no such hash, so it is hand-written like sha256 but over 64-bit words with 80 rounds. It is the digest Ed25519 signs over. Messages here are far under 2^64 bits, so the 128-bit length field's high half is always zero.

    sse_response

    fn sse_response(events : Array[ServerSentEvent], status? : Int, headers? : Array[(String, String)]) ->
    Response

    A text/event-stream response whose body is the framed events. Sets Cache-Control: no-cache and Connection: keep-alive, the headers an SSE endpoint sends so intermediaries don't buffer or close the stream.

    swagger_ui

    fn swagger_ui(spec_url? : String, title? : String) -> String

    A self-contained Swagger UI page rendering the document served at spec_url.

    text

    fn text(status : Int, body : String) ->
    Response

    A plain-text response.

    token_response

    fn token_response(access_token : String) ->
    Response

    The 200 token response body {"access_token": ..., "token_type": "bearer"} — the OAuth2 password-grant reply FastAPI's token endpoint returns.

    unprocessable

    A 422 Unprocessable Entity response whose application/json body lists the validation errors, exactly as FastAPI reports a failed request.

    validate_schema

    fn validate_schema(schema : Schema, value : Json, loc : Array[String], errs : Array[ValidationError]) -> Unit

    Validate a JSON value against the descriptor schema, appending FastAPI-shaped errors to errs (located at loc). This is what "the descriptor drives validation" means: the very tree that emits the OpenAPI body schema also decides whether an inbound body conforms — one source of truth, exactly as pydantic derives both from one model. A named object is validated against its inline fields, so no $ref resolution is needed here.

    validation_error_body

    fn validation_error_body(errors : Array[ValidationError]) -> Json

    The {"detail": [ ... ]} body FastAPI returns when request validation fails, built from a list of ValidationErrors.

    with_constraints

    fn with_constraints(j : Json, constraints : Array[Constraint], version : OpenApiVersion) -> Json

    Merge the constraints into a scalar/array schema object for OpenAPI emission. exclusiveMinimum / exclusiveMaximum are numeric under OpenAPI 3.1 (JSON-Schema 2020-12) but a boolean flag alongside minimum / maximum under Swagger 2.0 and OpenAPI 3.0, so the form is version-aware.