Sign in

    moonbitstack/moonapi/ctx does not have a README file

    Form

    A parsed form: its text fields and its files.

    Limits

    What a body is allowed to be.

    A body arrives from whoever sent it, so both bounds are needed: a million empty parts and one enormous part are different ways of asking a server to allocate more than it has.

    Upload

    A file from a multipart/form-data body.

    The content is held whole. Streaming a part to disk instead of into memory is what limits exists to make unnecessary until it is built.

    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::absorb

    fn BackgroundTasks::absorb(self : BackgroundTasks, other : BackgroundTasks) -> Unit

    Move other's queued tasks onto this queue (used when a mounted sub-app's tasks bubble up to the request that reached it), leaving other empty.

    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.

    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

    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. Spaces around a name or a value are the separators'; None if there is no Cookie header or the name is absent.

    Context::digest_credentials

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

    The credential of an Authorization: Digest <parameters> header — the comma-separated parameter list, verbatim (← FastAPI's HTTPDigest). None when the header is absent or names another scheme; the scheme name is matched case-insensitively, as RFC 7235 requires.

    Context::form

    The posted form — FastAPI's Form(...) and File(...) parameters.

    Reading the body is moonhttp/form's job; what belongs here is knowing that a request has a Content-Type and handing it over. None means the form broke its bounds and was refused whole: a handler given the first thousand parts of a larger form would be answering a request nobody sent. An empty form is the other answer, and means the request carried none.

    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 — and equally when the body is too large to be one, since a grant carries six fields and nothing that big is a login.

    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, limits? :
    Limits
    ) -> 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, limits? :
    Limits
    ) -> 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.

    Context::serve

    fn Context::serve(self : Context, content : Bytes, filename? : String, media_type? : String, inline? : Bool, etag? :
    Tag
    , modified? :
    Moment
    , ranges? : Bool) ->
    Response

    [file_response] with the request weighed first (RFC 9110 §13, §14):

    • 304 — the client's copy is current.
    • 412 — a precondition it stated does not hold.
    • 206 — it asked for part, one Content-Range or a multipart/byteranges.
    • 416 — it asked for what is not there.

    Everything turns on etag. Left out, one is hashed from the content, which costs a pass over the body; pass @conditional.stamp(length~, modified~) for one stat's worth instead, or any tag of your own.

    ranges=false withdraws the offer in Accept-Ranges.

    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).

    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.

    OAuth2PasswordRequestForm::equal

    OAuth2PasswordRequestForm::not_equal

    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.

    split_scopes

    fn split_scopes(s : String) -> Array[String]

    Split a space-delimited scope string into its scopes, dropping empty runs — the OAuth2 scope form field is one string like "read write".