Skip to content

Parameters and request bodies

2 min read

Use parameter attributes to identify input sources. Required inputs must be present; Option<T> accepts absence.

import system;
import system.webapi;
import system.json;

@json
class Message {
    public text: string;

    public constructor(text: string) {
        this.text = text;
    }
}

@router("")
class Routes {
    @get("/items/{id}")
    public static fun item(
        @path id: int,
        @query limit: Option<int>,
        @header("X-Request-ID") request_id: Option<string>
    ): string {
        return id.to_string()
            + ":" + limit.unwrap_or(10)
            .to_string() + ":" + request_id.unwrap_or("");
    }

    @post("/messages")
    public static fun message(@body body: Message): Message {
        return body;
    }
}

async fun main(): void {
    let app = WebApp();
    app.include_router(Routes.router()).unwrap();
    app.run("127.0.0.1", 8000).await.unwrap();
}

Input attributes

Attribute Reads from Accepted parameter types Name override
@path A {name} segment of the route path Scalars: string, bool, numbers @path("id")
@query The first query-string value with that name Scalars, or Option<T> of a scalar @query("page_size")
@header The first request header with that name Scalars, or Option<T> of a scalar @header("X-Request-ID")
@cookie A cookie with that name, not URL-decoded Scalars, or Option<T> of a scalar @cookie("session")
@body The whole request body An @json model, JsonValue, string, or byte[] —
@form A field of a multipart/form-data body Scalars, or Option<T> of a scalar @form("title")
@file A file part of a multipart/form-data body UploadedFile or Option<UploadedFile> @file("attachment")
@dep(provider) The value returned by a provider function The provider's value type —

Without an override, the parameter name is the input name. A parameter matching a {path} segment can bind without an explicit @path. A parameter that has no matching segment and no input attribute is a compile-time error. A handler can also take RequestContext to read the raw request, or ServerWebSocket on a @websocket route.

Scalar values are parsed with the declared type's parse, so @query limit: int rejects ?limit=ten.

For @body, a model uses strict validation. JsonValue parses any JSON without a model. string and byte[] give you the raw body for your own processing. A route cannot combine @body with @form or @file.

Defaults and constraints

Use @default_json("10") on a scalar parameter to supply a value when it is absent. The constraint attributes @minimum, @maximum, @min_length, @max_length, and @pattern also work on scalar parameters; the validation guide lists what each one checks.

Attribute arguments are positional. Ordinary function and constructor calls support named arguments.

Input errors

Situation Status Error type
A required parameter is missing 422 missing
A scalar value cannot be parsed as its type 422 type
A value fails a constraint attribute 422 minimum, max_length, and so on
A @body model fails validation 422 The validation code
A body is not valid JSON 422 json
A required header is missing inside a dependency provider 401 —
The request body cannot be read 400 —
A multipart form exceeds its limits 400 —

The 422 responses use the same {"detail": [{"loc", "type", "msg"}]} shape as model validation.

The server sets no limit on body size. Set one at your reverse proxy, or read large bodies with stream().

See the API lookup and complete example.