mbit

    A Gin-inspired web framework for MoonBit — fast, lightweight, idiomatic.

    web
    http
    framework
    router
    Download zip
    Version
    0.2.2
    License
    Apache-2.0
    Last updated
    3 months ago
    Downloads
    69

    #mbit

    mbit is a fast, lightweight web framework for MoonBit — batteries-included for production-ready Web development.

    A MoonBit web framework.

    MoonBit Tests License

    #Features

    • Trie-based router — :param and *wildcard matching, route groups, custom 404/405
    • Middleware — logger, recovery, CORS (dynamic origin), gzip, auth, rate-limit (token bucket / sliding window), secure headers (CSP/HSTS/CSRF), request ID, timing, metrics
    • Rendering — JSON / IndentedJSON / SecureJSON / PureJSON / AsciiJSON / JSONP, XML, YAML, HTML, SSE, streaming, file serving, content negotiation
    • Binding — JSON, query, form, header, path parameter binding with FromJson
    • Validation — declarative field validation (required, min, max, email, URL, custom)
    • Template — HTML template rendering with variable substitution
    • WebSocket — RFC 6455 upgrade, frame encode/decode, text/binary/close/ping/pong
    • File Upload — FileHeader metadata, streaming multipart parser, resumable upload (Content-Range), chunked processing with progress
    • Structured Logging — Logger::debug/info/warn/error/fatal, JSON / text format (with timestamp + fields), key-value fields, powered by moonbit-log
    • Configuration — environment variable binding, multi-environment (dev/staging/prod), ServerConfig
    • Security — CSRF token protection, security headers (CSP, HSTS, X-Frame-Options, X-XSS-Protection), configurable CORS with dynamic origin validation
    • Graceful Shutdown — shutdown() closes the HTTP server; built-in /health endpoint via app.health()
    • Static files — serve single files or entire directories
    • Metrics — in-memory request metrics collector (counts, latency, status codes)

    #Quick Start

    async fn main {
    let app = @mbit.default()

    app.get("/", [fn(ctx) { ctx.string(200, "Hello, mbit!") }])
    app.get("/hello/:name", [fn(ctx) {
    let name = ctx.param_default("name", "world")
    ctx.string(200, "Hello, " + name + "!")
    }])

    app.run("0.0.0.0:8080")
    }

    #Installation

    Add to moon.pkg:

    import {
    "RabitLogic/mbit",
    }

    #Routing

    #Basic Routes

    app.get("/users", [handler])
    app.post("/users", [handler])
    app.put("/users/:id", [handler])
    app.delete("/users/:id", [handler])
    app.patch("/users/:id", [handler])
    app.head("/health", [handler])
    app.options("/api", [handler])

    // Match all HTTP methods
    app.any("/health", [handler])

    // Register with explicit methods
    app.handle([GET, POST], "/multi", [handler])

    #Path Parameters

    app.get("/users/:id", [fn(ctx) {
    let id = ctx.param("id") // Some("42")
    let id_int = ctx.param_int("id") // Some(42)
    let id_str = ctx.param_default("id", "0")
    let all = ctx.params_all() // Map of all params
    }])

    #Wildcard Routes

    app.get("/files/*filepath", [fn(ctx) {
    let path = ctx.param_default("filepath", "")
    ctx.string(200, "Requested: " + path)
    }])

    #Route Groups

    let api = app.group("/api")
    api.get("/status", [status_handler])
    api.post("/login", [login_handler])

    // Nested groups
    let admin = api.group("/admin")
    admin.use(auth_middleware)
    admin.get("/dashboard", [dashboard_handler])

    #Custom Error Handlers

    app.no_route([fn(ctx) {
    ctx.json(404, Json::object({"error": Json::string("Not Found")}))
    }])
    app.no_method([fn(ctx) {
    ctx.json(405, Json::object({"error": Json::string("Method Not Allowed")}))
    }])

    #Redirects & Path Options

    app.redirect_trailing_slash(true) // /foo/ → /foo
    app.redirect_fixed_path(true) // case-insensitive fix
    app.remove_extra_slash(true) // //foo → /foo
    app.handle_method_not_allowed(false) // disable 405

    #Middleware

    #Built-in Middleware

    MiddlewareDescription
    logger()Logs method, path, status, latency
    structured_logger(config)Structured JSON/text logging with log levels + fields
    recovery()Catches panics, returns 500
    cors(config)CORS headers with dynamic origin validation
    gzip(config)Response compression
    request_id()X-Request-Id header
    timing()X-Response-Time header
    secure(config)Security headers (helmet-like) + CSRF protection
    auth()Basic auth
    rate_limit(config)Rate limiting (token bucket / sliding window / fixed window)
    body_size_limit(bytes)Reject large payloads
    metrics_middleware(collector)Request metrics collection (counts, latency, status codes)

    #Using Middleware

    // Global middleware (applies to all routes)
    app.use(logger())
    app.use(recovery())
    app.use(cors(CORSConfig::default()))

    // Group middleware (applies to routes in that group)
    let api = app.group("/api")
    api.use(auth())
    api.use(rate_limit(RateLimitConfig::{ max_requests: 100, window_secs: 60 }))

    #Logger

    app.use(logger())
    // Output: [mbit] GET /api/users -> 200 (12ms)

    #CORS

    // Static origins
    app.use(cors(CORSConfig::{
    allow_origins: ["https://example.com"],
    allow_methods: ["GET", "POST"],
    allow_headers: ["Content-Type", "Authorization"],
    allow_credentials: true,
    max_age: 3600,
    expose_headers: ["Content-Length"],
    }))

    // Dynamic origin validation
    app.use(cors(CORSConfig::with_origin_fn(fn(origin) {
    origin.has_suffix(".trusted.com") || origin == "https://app.example.com"
    })))

    #Gzip

    app.use(gzip(GzipConfig::{ min_length: 512, level: 6 }))

    #Rate Limiting

    // Fixed window (default)
    app.use(rate_limit(RateLimitConfig::{
    max_requests: 50,
    window_secs: 60,
    }))

    // Token bucket (smooth rate limiting)
    app.use(rate_limit(RateLimitConfig::token_bucket(
    RateTokenBucket::{ rate: 10.0, burst: 20 },
    )))

    // Per-route rate limiting
    app.use(rate_limit(
    RateLimitConfig::default().when(fn(ctx) { ctx.path().has_prefix("/api/") })
    ))

    // Per-user rate limiting
    app.use(rate_limit(
    RateLimitConfig::default().with_key(fn(ctx) { ctx.param_default("api_key", "unknown") })
    ))

    #Secure Headers

    // Security headers only
    app.use(secure(SecureConfig::default()))

    // With CSRF protection
    app.use(secure(SecureConfig::default().with_csrf()))
    // Sets: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection,
    // Content-Security-Policy, Referrer-Policy, HSTS, CSRF token

    #WebSocket

    app.get("/ws", [websocket_handler(fn(ws) {
    ws.send_text("Connected!")
    loop {
    match ws.read() {
    Some(WebSocketMessage::Text(t)) => ws.send_text("Echo: " + t)
    Some(WebSocketMessage::Close(_, _)) => break
    _ => ()
    }
    }
    })])

    // Manual upgrade
    app.get("/ws2", [fn(ctx) {
    let ws = upgrade_websocket(ctx)
    match ws {
    Some(ws) => ws.send_text("Hello via WebSocket!")
    None => ctx.abort_with_status(400, "WebSocket upgrade failed")
    }
    }])

    #Structured Logging

    // Request logging with structured fields
    app.use(structured_logger(StructuredLogConfig::default()))

    // Log with levels and fields anywhere
    Logger::info("User logged in", fields=[("user_id", "42"), ("ip", ctx.client_ip())])
    Logger::error("Database timeout", fields=[("query", "SELECT ...")])
    Logger::debug("Cache miss", fields=[("key", "user:42")])

    // Output (text format):
    // [2026-07-22T10:00:00] [INFO] User logged in app=mbit env=development user_id=42 ip=10.0.0.1

    // Configure
    Logger::set_level(Info)
    Logger::set_format(JSONFormat)

    // Metrics collection
    let metrics = MetricsCollector::new()
    app.use(metrics_middleware(metrics))
    app.get("/metrics", [fn(ctx) { ctx.json(200, metrics.to_json()) }])

    #Custom Middleware

    fn auth_middleware() -> Handler {
    async fn(ctx) {
    match ctx.header("Authorization") {
    Some(token) => {
    ctx.set("token", Json::string(token)) // store for downstream handlers
    ctx.next() // continue the chain
    }
    None => ctx.abort_with_status(401, "Unauthorized")
    }
    }
    }

    app.use(auth_middleware())

    #Context

    The Context carries everything about the current request.

    #Request Info

    ctx.http_method() // "GET", "POST", etc.
    ctx.path() // "/users/42"
    ctx.full_path() // "/users/42?page=1"
    ctx.client_ip() // client IP address
    ctx.content_type() // "application/json"
    ctx.header("Accept") // optional header value
    ctx.is_websocket() // true if upgrade request

    #Query Parameters

    ctx.query("page") // Some("1")
    ctx.query_default("page", "1") // "1"
    ctx.query_int("page") // Some(1)
    ctx.query_int64("id") // Some(42L)
    ctx.query_map() // all query params as Map
    ctx.query_array("tags") // ["a", "b"] for ?tags=a&tags=b

    #Request Body

    ctx.body_string() // raw body as string
    ctx.body_json() // parsed as Json

    // Form data (application/x-www-form-urlencoded)
    ctx.post_form("name") // Some("Alice")
    ctx.default_post_form("name", "unknown")
    ctx.post_form_map() // all form fields

    #Response Rendering

    // JSON
    ctx.json(200, Json::object({"message": Json::string("ok")}))
    ctx.json_indented(200, data)
    ctx.json_secure(200, data) // with while(1) prefix
    ctx.json_pure(200, data) // no escaping

    // XML & YAML
    ctx.xml(200, "root", data)
    ctx.yaml(200, data)

    // HTML & Text
    ctx.html(200, "<h1>Hello</h1>")
    ctx.string(200, "plain text")

    // Redirect
    ctx.redirect(301, "/new-path")

    // File
    ctx.file("path/to/file.pdf")

    // SSE (Server-Sent Events)
    ctx.sse("event: message\ndata: hello\n\n")

    // Streaming
    ctx.stream(200, fn(writer) {
    writer("chunk1")
    writer("chunk2")
    })

    #Context Store (Key-Value)

    ctx.set("user_id", Json::string("42"))
    let user_id = ctx.get("user_id") // Some(Json::String("42"))
    ctx.get_string("user_id") // Some("42")

    #Response Headers

    ctx.set_header("X-Custom", "value")
    ctx.status_code // current status (default 200)
    ctx.is_written() // has response been sent

    #Binding

    Bind request data to structs using FromJson.

    struct LoginRequest {
    username : String
    password : String
    } derive(@json.FromJson)

    // JSON body
    app.post("/login", [async fn(ctx) {
    let req : LoginRequest? = bind_json(ctx)
    match req {
    Some(r) => ctx.json(200, Json::object({"user": Json::string(r.username)}))
    None => ctx.abort_with_status(400, "Invalid JSON")
    }
    }])

    // Query string: GET /search?q=hello&page=1
    struct SearchQuery {
    q : String
    page : String
    } derive(@json.FromJson)
    let query = bind_query(ctx)

    // Form body
    let form = bind_form(ctx)

    // Headers
    let headers = bind_header(ctx)

    // Path params
    let params = bind_path(ctx)

    #MustBind (aborts on failure)

    let req = ctx.must_bind_json::[LoginRequest]() // aborts with 400 on failure

    #Validation

    let rules = [
    FieldRules::new("username", [Required, Min(3), Max(20)]),
    FieldRules::new("email", [Required, Email]),
    FieldRules::new("age", [Min(0), Max(150)]),
    ]

    let errors = validate_map(values, rules)
    if errors.length() > 0 {
    ctx.abort_with_status(422, "Validation failed")
    return
    }

    #Available Rules

    RuleDescription
    RequiredMust not be empty
    Min(n)Minimum length or numeric value
    Max(n)Maximum length or numeric value
    Len(n)Exact length
    Pattern(regex)Must match pattern
    OneOf([...])Must be one of the values
    EmailBasic email check
    URLBasic URL check
    Custom(fn)Custom validation function

    #Templates

    app.set_html_template("welcome", "<html><body>Hello {{.Name}}!</body></html>")
    app.set_template_delims("{{", "}}")

    // In handler
    ctx.html_template("welcome", {"Name": "World"})

    #Static Files

    // Single file
    app.static_file("/favicon.ico", "./public/favicon.ico")

    // Directory
    app.static_("/static", "./public")

    // Directory with wildcard
    app.static_fs("/assets", "./public")

    #Server

    // Basic
    app.run("0.0.0.0:8080")

    // Default (logs port)
    app.run_default()

    // Built-in health check endpoint
    app.health() // registers GET /health → {"status":"ok","service":"mbit"}

    // Graceful shutdown — call shutdown() from a handler or signal
    app.run_with_shutdown(":8080")
    // Elsewhere: app.shutdown() closes the server

    // Trigger shutdown from signal handler
    app.shutdown(cleanup=fn() {
    println("Cleaning up resources...")
    })

    #Debug

    let routes = app.routes()
    for r in routes {
    println(r.meth + " " + r.path)
    }

    println(app.trees()) // ASCII tree of the router

    #Configuration

    let engine = Engine::new()
    engine.max_multipart_memory(32 * 1024 * 1024) // 32 MB
    engine.redirect_trailing_slash(true)
    engine.handle_method_not_allowed(true)
    engine.remove_extra_slash(true)
    engine.redirect_fixed_path(false)
    let app = Mbit::new(engine)

    #Complete Example

    async fn main {
    let app = @mbit.default()

    // Global middleware
    app.use(logger())
    app.use(recovery())
    app.use(cors(CORSConfig::default()))
    app.use(request_id())
    app.use(timing())

    // Custom 404
    app.no_route([fn(ctx) {
    ctx.json(404, Json::object({"error": Json::string("Not Found")}))
    }])

    // Routes
    app.get("/", [fn(ctx) { ctx.string(200, "Hello, mbit!") }])
    app.get("/hello/:name", [fn(ctx) {
    ctx.string(200, "Hello, " + ctx.param_default("name", "world") + "!")
    }])

    app.post("/api/echo", [async fn(ctx) {
    match ctx.body_json() {
    Some(json) => ctx.json(200, json)
    None => ctx.abort_with_status(400, "Invalid JSON")
    }
    }])

    // Protected group
    let api = app.group("/api")
    api.use(fn(ctx) {
    match ctx.header("Authorization") {
    Some(_) => ctx.next()
    None => ctx.abort_with_status(401, "Unauthorized")
    }
    })
    api.get("/users", [list_users])

    // Static files
    app.static_("/static", "./public")

    app.run("0.0.0.0:8080")
    }

    #File Upload

    // Single file with metadata
    match ctx.form_file("avatar") {
    Some(file) => {
    println("Received: \{file.filename} (\{file.size} bytes)")
    ctx.save_uploaded_file(file, "./uploads/" + file.filename)
    }
    None => ctx.abort_with_status(400, "No file uploaded")
    }

    // All multipart fields
    let form = ctx.multipart_form()
    for field_name, file in form {
    println("\{field_name}: \{file.filename} (\{file.content_type})")
    }

    // Resumable upload (supports Content-Range)
    ctx.save_uploaded_file_resumable(file, "./uploads/video.mp4")

    // Chunked processing with progress callback
    let mut progress = UploadProgress::new(file.size, 1024 * 1024)
    ctx.save_uploaded_chunked(file, "./large.bin",
    ChunkedWriteConfig::default(),
    fn(chunk, i, total) {
    progress = progress.update(chunk.length().to_int64())
    println("Progress: \{progress.percentage.to_int()}%")
    true // continue
    })

    #Configuration

    // Load from environment variables (APP_PORT, APP_DATABASE_URL, etc.)
    let config = Config::from_env()
    let port = config.get_int("port", default=8080L)

    // Strongly-typed server config
    let srv = ServerConfig::from_env()
    app.set_max_multipart_memory(srv.max_upload_size)

    // Check environment
    if config.is_production() {
    Logger::set_level(Info)
    Logger::set_format(JSONFormat)
    }

    #API Reference

    MethodDescription
    Mbit::default()Create with logger + recovery
    Mbit::new(engine)Create with custom engine
    get/post/put/delete/patch/head/options(path, handlers)Route registration
    any(path, handlers)Match all methods
    handle(methods, path, handlers)Match specific methods
    use(mw)Add global middleware
    group(prefix)Create route group
    no_route(handlers)404 handler
    no_method(handlers)405 handler
    static_file(path, file)Serve single file
    static_(prefix, root)Serve directory
    static_fs(prefix, root)Serve directory with wildcard
    set_html_template(name, content)Register template
    max_multipart_memory(bytes)Set upload limit
    health()Register GET /health endpoint
    redirect_trailing_slash(bool)Auto-redirect trailing slashes
    handle_method_not_allowed(bool)Enable 405 responses
    remove_extra_slash(bool)Normalize double slashes
    run(addr)Start server
    run_default()Start on default port
    run_with_shutdown(addr, on_shutdown)Start with graceful shutdown
    shutdown()Trigger shutdown
    routes()List all routes
    trees()Print route tree

    #Context

    MethodReturnsDescription
    http_method()StringHTTP method
    path()StringRequest path
    full_path()StringPath with query string
    param(key)String?Path parameter
    param_default(key, default)StringPath parameter with fallback
    param_int(key)Int?Path parameter as Int
    param_int64(key)Int64?Path parameter as Int64
    params_all()Map[String,String]All path params
    query(key)String?Query parameter
    query_default(key, default)StringQuery with fallback
    query_int(key)Int?Query as Int
    query_int64(key)Int64?Query as Int64
    query_map()Map[String,String]All query params
    query_array(key)Array[String]Multi-value query
    header(key)String?Request header
    client_ip()StringClient IP
    content_type()StringContent-Type
    body_string()StringRaw body
    body_json()Json?Parsed JSON body
    post_form(key)String?Form field
    json(code, data)UnitRender JSON
    xml(code, root, data)UnitRender XML
    yaml(code, data)UnitRender YAML
    html(code, html)UnitRender HTML
    string(code, text)UnitRender plain text
    data(code, contentType, body)UnitRender custom
    redirect(code, url)UnitRedirect
    file(path)UnitServe file
    sse(event)UnitSSE event
    stream(code, writerFn)UnitStream response
    set_header(key, value)UnitSet response header
    set(key, value)UnitStore value
    get(key)Json?Get stored value
    get_string(key)String?Get stored string
    abort()UnitStop handler chain
    abort_with_status(code, msg)UnitAbort with JSON error
    is_aborted()BoolCheck if aborted
    next()UnitContinue to next handler
    must_bind_json::[T]()TBind JSON or abort
    negotiate_format(offers)String?Content negotiation
    is_websocket()BoolWebSocket upgrade check

    #License

    MIT

    ChunkCallback

    type ChunkCallback = (String, Int, Int) -> Bool

    Callback type for processing file chunks. Called with each chunk of data; return false to abort.

    Handler

    type Handler = async (Context) -> Unit

    Handler function signature. All route handlers and middleware share this signature.

    IRoutes

    pub trait IRoutes {
    fn get(String, Array[async (Context) -> Unit]) -> Unit
    fn post(String, Array[async (Context) -> Unit]) -> Unit
    fn put(String, Array[async (Context) -> Unit]) -> Unit
    fn del(String, Array[async (Context) -> Unit]) -> Unit
    fn patch(String, Array[async (Context) -> Unit]) -> Unit
    fn head(String, Array[async (Context) -> Unit]) -> Unit
    fn options(String, Array[async (Context) -> Unit]) -> Unit
    fn any(String, Array[async (Context) -> Unit]) -> Unit
    fn handle(Array[Method], String, Array[async (Context) -> Unit]) -> Unit
    fn use_mw(async (Context) -> Unit) -> Unit
    }

    IRoutes defines the common route registration methods.

    AppEnvironment

    pub(all) enum AppEnvironment {
    Development
    Staging
    Production
    Test
    } derive(Eq, Hash,
    Debug
    )

    Supported deployment environments.

    AppEnvironment::from_string

    fn AppEnvironment::from_string(s : String) -> AppEnvironment

    Parse environment from string.

    AppEnvironment::to_string

    fn AppEnvironment::to_string(self : AppEnvironment) -> String

    Convert environment to string.

    BasicAuthConfig

    pub(all) struct BasicAuthConfig {
    accounts : Map[String, String]
    realm : String
    }

    Basic authentication configuration.

    CORSConfig

    pub(all) struct CORSConfig {
    allow_origins : Array[String]
    allow_origin_fn : (String) -> Bool?
    allow_methods : Array[String]
    allow_headers : Array[String]
    expose_headers : Array[String]
    allow_credentials : Bool
    max_age : Int
    vary_origin : Bool
    allow_private_network : Bool
    }

    CORS configuration with dynamic origin support.

    CORSConfig::default

    fn CORSConfig::default() -> CORSConfig

    Default CORS config (permissive).

    CORSConfig::with_origin_fn

    fn CORSConfig::with_origin_fn(allow_origin_fn : (String) -> Bool) -> CORSConfig

    Create a CORS config with a dynamic origin validator. The validator function receives the Origin header and returns true if allowed.

    let cfg = CORSConfig::with_origin_fn(fn(origin) { origin.has_suffix(".mydomain.com") || origin == "https://app.mydomain.com" }) app.use(cors(cfg))

    ChunkedWriteConfig

    pub(all) struct ChunkedWriteConfig {
    chunk_size : Int
    process_callback : Bool
    }

    Configuration for chunked file processing.

    ChunkedWriteConfig::default

    Default chunked write config (1 MB chunks).

    Config

    pub(all) struct Config {
    values : Map[String, ConfigValue]
    env : AppEnvironment
    env_prefix : String
    }

    A configuration store backed by environment variables and defaults.

    Config::environment

    fn Config::environment(self : Config) -> AppEnvironment

    Current environment.

    Config::from_env

    fn Config::from_env(prefix? : String) -> Config

    Load configuration from environment variables. Reads all env vars matching the prefix and stores them as ConfigValues.

    let cfg = Config::from_env() let port = cfg.get_int("PORT", default=8080)

    Config::get_bool

    fn Config::get_bool(self : Config, key : String, default? : Bool) -> Bool

    Get a bool config value, or a default if not set.

    Config::get_float

    fn Config::get_float(self : Config, key : String, default? : Double) -> Double

    Get a float config value, or a default if not set.

    Config::get_int

    fn Config::get_int(self : Config, key : String, default? : Int64) -> Int64

    Get an int config value, or a default if not set.

    Config::get_string

    fn Config::get_string(self : Config, key : String, default? : String) -> String

    Get a string config value, or a default if not set.

    Config::has

    fn Config::has(self : Config, key : String) -> Bool

    Check if a config key exists.

    Config::is_development

    fn Config::is_development(self : Config) -> Bool

    Whether the current environment is development.

    Config::is_production

    fn Config::is_production(self : Config) -> Bool

    Whether the current environment is production.

    Config::keys

    fn Config::keys(self : Config) -> Array[String]

    Get all config keys.

    Config::new

    fn Config::new(env? : AppEnvironment) -> Config

    Create an empty config with the given environment.

    Config::to_json

    fn Config::to_json(self : Config) -> Json

    Export config as JSON (for /debug/config endpoints).

    ConfigValue

    pub(all) enum ConfigValue {
    ConfigString(String)
    ConfigInt(Int64)
    ConfigBool(Bool)
    ConfigFloat(Double)
    } derive(
    Debug
    )

    Config value types supported by the configuration system.

    ConfigValue::to_string

    fn ConfigValue::to_string(self : ConfigValue) -> String

    Convert ConfigValue to string.

    ContentRange

    pub(all) struct ContentRange {
    start : Int64
    end : Int64
    total : Int64
    }

    Parsed Content-Range header info for resumable uploads.

    Context

    pub(all) struct Context {
    req :
    Request

    reader : &
    Reader

    conn : ResponseConn
    params : Map[String, String]
    query_cache : Map[String, String]?
    body : String?
    post_form_cache : Map[String, String]?
    handlers : Array[async (Context) -> Unit]
    index : Int
    status_code : Int
    written : Bool
    aborted : Bool
    size : Int
    store : Map[String, Json]
    errors : Array[String]
    headers : Map[String, String]
    }

    Context holds all per-request state.

    Context::abort

    fn Context::abort(self : Context) -> Unit

    Abort the handler chain. No further handlers will be executed. Does NOT write a response — you should call a render method separately.

    Context::abort_with_error

    async fn Context::abort_with_error(self : Context, code : Int, err : String) -> Unit

    AbortWithError aborts with a status code and adds an error to the context.

    Context::abort_with_status

    async fn Context::abort_with_status(self : Context, code : Int, message : String) -> Unit

    Abort with a specific status code and JSON error message.

    Context::abort_with_status_json

    async fn Context::abort_with_status_json(self : Context, code : Int, message : String) -> Unit

    AbortWithStatusJSON aborts with a JSON error response. Convenience method combining abort + JSON error response.

    Context::add_error

    fn Context::add_error(self : Context, err : String) -> Unit

    Add an error to the context's error list.

    Context::ascii_json

    async fn Context::ascii_json(self : Context, code : Int, data : Json) -> Unit

    AsciiJSON serializes JSON with ASCII-only characters (escapes non-ASCII).

    Context::bind_api

    async fn[T :
    FromJson
    ] Context::bind_api(self : Context) -> T?

    Bind auto-detects content type and binds, aborting on failure. See c.Bind() equivalent.

    Context::bind_form_api

    async fn[T :
    FromJson
    ] Context::bind_form_api(self : Context) -> T?

    BindForm binds form body. On failure, aborts with 400.

    Context::bind_header_api

    async fn[T :
    FromJson
    ] Context::bind_header_api(self : Context) -> T?

    BindHeader binds request headers. On failure, aborts with 400.

    Context::bind_json_api

    async fn[T :
    FromJson
    ] Context::bind_json_api(self : Context) -> T?

    BindJSON binds the request body as JSON. On failure, aborts with 400. See c.BindJSON() equivalent.

    Context::bind_query_api

    async fn[T :
    FromJson
    ] Context::bind_query_api(self : Context) -> T?

    BindQuery binds query parameters. On failure, aborts with 400.

    Context::body_json

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

    Parse the request body as JSON.

    Context::body_string

    async fn Context::body_string(self : Context) -> String

    Read the full request body as a string (lazy, cached).

    Context::client_ip

    fn Context::client_ip(self : Context) -> String

    The client's IP address.

    Context::content_range

    fn Context::content_range(self : Context) -> ContentRange?

    Check if the request contains a Content-Range header for resumable upload.

    Context::content_type

    fn Context::content_type(self : Context) -> String

    Returns the value of the Content-Type request header.

    Context::cookie

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

    Get a cookie value by name from the request headers.

    let session = ctx.cookie("session_id")

    Context::data

    async fn Context::data(self : Context, code : Int, content_type : String, body : String) -> Unit

    Send a response with custom content type.

    Context::data_from_reader

    async fn Context::data_from_reader(self : Context, code : Int, content_length : Int64, content_type : String, reader : &
    Reader
    ) -> Unit

    DataFromReader writes the specified content type and reads data from the given reader, sending it as the response body.

    ctx.data_from_reader(200, content_length, "application/octet-stream", reader)

    Context::default_post_form

    async fn Context::default_post_form(self : Context, key : String, default : String) -> String

    Get a post form value with a default fallback.

    Context::default_query

    fn Context::default_query(self : Context, key : String, default : String) -> String

    DefaultQuery returns the query value or a default. See c.DefaultQuery() equivalent.

    Context::end_stream

    async fn Context::end_stream(self : Context) -> Unit

    End a stream response.

    Context::error

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

    Error returns the last error in the context's error list. See c.Errors.Last() equivalent.

    Context::errors_all

    fn Context::errors_all(self : Context) -> Array[String]

    All collected errors.

    Context::file

    async fn Context::file(self : Context, code : Int, content_type : String, file_path : String) -> Unit

    Send a file as the response body with the given content type. The file content is read from disk and sent inline.

    ctx.file(200, "application/pdf", "./reports/annual.pdf")

    Context::file_attachment

    async fn Context::file_attachment(self : Context, file_path : String, filename? : String) -> Unit

    Send a file as an attachment (forces download with Content-Disposition).

    See c.FileAttachment() equivalent.

    Context::file_from_fs

    async fn Context::file_from_fs(self : Context, file_path : String) -> Unit

    FileFromFS serves a file from a filesystem path. See c.FileFromFS() equivalent.

    Context::flush

    async fn Context::flush(self : Context) -> Unit

    Flush writes any buffered data to the client. See c.Writer.Flush() equivalent.

    Context::form_file

    async fn Context::form_file(self : Context, field_name : String) -> FileHeader?

    FormFile returns the metadata and content of an uploaded file from a multipart/form-data request.

    Returns Some(FileHeader) with filename, content, content-type, and size, or None if the field is not present or not a multipart request.

    match ctx.form_file("avatar") { Some(file) => { println("Received: \{file.filename} (\{file.size} bytes)") ctx.save_uploaded_file(file, "./uploads/" + file.filename) } None => ctx.abort_with_status(400, "No file uploaded") }

    Context::full_path

    fn Context::full_path(self : Context) -> String

    The full request path including query string.

    Context::get

    fn Context::get(self : Context, key : String) -> Json?

    Retrieve a value from the context store.

    Context::get_bool

    fn Context::get_bool(self : Context, key : String) -> Bool?

    Retrieve a Bool value from the context store.

    Context::get_duration

    fn Context::get_duration(self : Context, key : String) -> Int64?

    GetDuration retrieves a duration value (stored as Int64 nanoseconds) from the store. See c.GetDuration() equivalent.

    Context::get_float64

    fn Context::get_float64(self : Context, key : String) -> Double?

    Retrieve a Float64 value from the context store.

    Context::get_header

    fn Context::get_header(self : Context, key : String) -> String?

    GetHeader is an alias for header() — for API compatibility. See c.GetHeader() equivalent.

    Context::get_int64

    fn Context::get_int64(self : Context, key : String) -> Int64?

    Retrieve an Int64 value from the context store.

    Context::get_post_form_array

    async fn Context::get_post_form_array(self : Context, key : String) -> Array[String]

    GetPostFormArray returns all values for a post form parameter key. e.g., form with tag=a&tag=b → ["a", "b"]

    Context::get_post_form_file

    async fn Context::get_post_form_file(self : Context, field_name : String) -> String?

    GetPostFormFile returns the original filename from a multipart form upload. Returns the filename extracted from the Content-Disposition header.

    Context::get_query_array

    fn Context::get_query_array(self : Context, key : String) -> Array[String]

    GetQueryArray returns all values for a query parameter key. e.g., ?tag=a&tag=b → ["a", "b"]

    Context::get_raw_data

    async fn Context::get_raw_data(self : Context) -> String

    GetRawData returns the raw request body as a string. See c.GetRawData() equivalent.

    Context::get_string

    fn Context::get_string(self : Context, key : String) -> String?

    Retrieve a string value from the context store.

    Context::get_string_map

    fn Context::get_string_map(self : Context, key : String) -> Map[String, String]?

    GetStringMap returns a string map from the store. See c.GetStringMap() equivalent.

    Context::get_string_map_string

    fn Context::get_string_map_string(self : Context, key : String) -> Map[String, String]?

    GetStringMapString returns a flat string map from the store. See c.GetStringMapString() equivalent.

    Context::get_time

    fn Context::get_time(self : Context, key : String) -> Int64?

    GetTime retrieves a time value (stored as Int64 nanoseconds) from the store. See c.GetTime() equivalent.

    Context::handler_name

    fn Context::handler_name(self : Context) -> String

    HandlerName returns the name of the last handler in the chain. Useful for debugging and logging.

    Context::handler_names

    fn Context::handler_names(self : Context) -> Array[String]

    HandlerNames returns the names of all handlers in the chain.

    Context::header

    fn Context::header(self : Context, key : String) -> String?

    A request header value, if present.

    Context::html

    async fn Context::html(self : Context, code : Int, html : String) -> Unit

    Render HTML.

    Context::html_template

    async fn Context::html_template(self : Context, code : Int, name : String, data : Map[String, String]) -> Unit

    Render an HTML template response.

    Context::http_method

    fn Context::http_method(self : Context) -> String

    The HTTP method of the request (e.g., "GET", "POST").

    Context::indented_json

    async fn Context::indented_json(self : Context, code : Int, data : Json) -> Unit

    Render indented / pretty-printed JSON. Same as json() but with indentation for readability.

    Context::is_aborted

    fn Context::is_aborted(self : Context) -> Bool

    Whether the chain has been aborted.

    Context::is_websocket

    fn Context::is_websocket(self : Context) -> Bool

    Check if the request is a WebSocket upgrade request.

    Context::is_written

    fn Context::is_written(self : Context) -> Bool

    Whether the response has been written.

    Context::json

    async fn Context::json(self : Context, code : Int, data : Json) -> Unit

    Render JSON. This finalizes the response; no further writes are allowed.

    Context::jsonp

    async fn Context::jsonp(self : Context, code : Int, data : Json) -> Unit

    Render JSONP — wraps JSON in a callback function. The callback name is taken from the callback query parameter.

    See c.JSONP() equivalent.

    Context::keys

    fn Context::keys(self : Context) -> Array[String]

    Keys returns all keys in the context store. See c.Keys equivalent.

    Context::multipart_form

    async fn Context::multipart_form(self : Context) -> Map[String, FileHeader]

    MultipartForm parses the entire multipart/form-data body and returns a map of field name → FileHeader for all file fields.

    This is useful for forms with multiple file uploads.

    Context::must_bind

    async fn[T :
    FromJson
    ] Context::must_bind(self : Context) -> T

    Like should_bind but aborts with 400 if binding fails.

    Context::must_bind_with

    async fn[T] Context::must_bind_with(self : Context, binder : async (Context) -> T?) -> T

    MustBindWith binds using a custom binding function, aborting on failure. See c.MustBindWith() equivalent.

    Context::must_get

    fn Context::must_get(self : Context, key : String) -> Json

    MustGet returns the value for the given key if it exists, otherwise panics. Use this when the value is guaranteed to have been set by middleware.

    Context::negotiate_format

    async fn Context::negotiate_format(self : Context, offered : Array[String]) -> String?

    Content negotiation: returns the best accepted content type from the Accept header. If none matches, sets a 406 status and returns None.

    match ctx.negotiate_format(["application/json", "text/html"]) { Some("application/json") => ctx.json(200, data) Some("text/html") => ctx.html(200, "<h1>Hello</h1>") _ => () // 406 already set }

    Context::new

    Create a new Context from the raw HTTP components and handler chain.

    Context::next

    async fn Context::next(self : Context) -> Unit

    Next should be called only inside middleware. It executes the remaining handlers in the chain.

    This is the heart of the middleware chain pattern:
    • Middleware runs code BEFORE c.next()
    • c.next() yields to the next handler and waits for it
    • Middleware runs code AFTER c.next() returns

    Context::no_content

    async fn Context::no_content(self : Context) -> Unit

    Send a 204 No Content response.

    Context::param

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

    Get a path parameter by name. Route patterns like /api/article/:id populate ctx.param("id").

    Context::param_default

    fn Context::param_default(self : Context, key : String, default : String) -> String

    Get a path parameter, returning a default value if not present.

    Context::param_int64

    fn Context::param_int64(self : Context, key : String) -> Int64?

    Get a path parameter parsed as Int64.

    Context::params_all

    fn Context::params_all(self : Context) -> Map[String, String]

    All parsed path parameters.

    Context::path

    fn Context::path(self : Context) -> String

    The request path without the query string.

    Context::post_form

    async fn Context::post_form(self : Context, key : String) -> String?

    Get a value from the POST form body. Parses the body as application/x-www-form-urlencoded on first access.

    Context::post_form_map

    async fn Context::post_form_map(self : Context) -> Map[String, String]

    PostFormMap returns all POST form parameters as a map.

    Context::proto_buf

    async fn Context::proto_buf(self : Context, code : Int, obj : Json) -> Unit

    ProtoBuf renders a protobuf response (placeholder — returns JSON).

    Context::pure_json

    async fn Context::pure_json(self : Context, code : Int, data : Json) -> Unit

    Render PureJSON — writes JSON without escaping HTML characters. (In MoonBit, the default JSON stringify does not escape HTML, so this is equivalent to json() — included for API completeness.)

    Context::query

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

    Get a query parameter by name.

    Context::query_default

    fn Context::query_default(self : Context, key : String, default : String) -> String

    Get a query parameter with a default fallback.

    Context::query_int

    fn Context::query_int(self : Context, key : String) -> Int?

    Get a query parameter parsed as Int.

    Context::query_int64

    fn Context::query_int64(self : Context, key : String) -> Int64?

    Get a query parameter parsed as Int64.

    Context::query_map

    fn Context::query_map(self : Context) -> Map[String, String]

    QueryMap returns all query parameters as a map. See c.QueryMap() equivalent.

    Context::raw_data

    async fn Context::raw_data(self : Context) -> String

    Get the raw request body as a string.

    Context::redirect

    async fn Context::redirect(self : Context, code : Int, location : String) -> Unit

    Redirect to another URL.

    Context::render

    async fn Context::render(self : Context, code : Int, render_type : RenderType, data : Json) -> Unit

    Render dispatches to the appropriate renderer based on content type. See c.Render() equivalent.

    Context::request

    Request returns the raw HTTP request. See c.Request equivalent.

    Context::reset

    fn Context::reset(self : Context) -> Unit

    Reset the context state for reuse (used by HandleContext).

    Context::save_uploaded_chunked

    async fn Context::save_uploaded_chunked(self : Context, file : FileHeader, dst : String, config : ChunkedWriteConfig, callback : (String, Int, Int) -> Bool) -> Unit

    Save an uploaded file in chunks, processing each chunk via callback.

    Useful for very large files where you want to:
    • Track progress
    • Process data incrementally (e.g., hash, validate)
    • Avoid loading the entire file into memory at once (still uses body_string for the initial read, but writes and processes incrementally)

    let mut bytes_written = 0L ctx.save_uploaded_chunked(file, "./uploads/large.bin", ChunkedWriteConfig::default(), fn(chunk, index, total_chunks) { bytes_written = bytes_written + chunk.length().to_int64() println("chunk \{index}/\{total_chunks}") true // continue })

    Context::save_uploaded_file

    async fn Context::save_uploaded_file(self : Context, file : FileHeader, dst : String) -> Unit

    Save an uploaded file to the specified destination path.

    match ctx.form_file("avatar") { Some(file) => ctx.save_uploaded_file(file, "./uploads/" + file.filename) None => ctx.abort_with_status(400, "No file uploaded") }

    Context::save_uploaded_file_content

    async fn Context::save_uploaded_file_content(self : Context, content : String, dst : String) -> Unit

    Convenience: save raw content to disk (backward compatible).

    Context::save_uploaded_file_resumable

    async fn Context::save_uploaded_file_resumable(self : Context, file : FileHeader, dst : String) -> Unit

    Save an uploaded file with resumable upload support.

    If the request includes a Content-Range header, the function appends the content to the existing file (resume). Otherwise, it overwrites (new upload).

    match ctx.form_file("video") { Some(file) => { ctx.save_uploaded_file_resumable(file, "./uploads/video.mp4") } None => ctx.abort_with_status(400, "No file") }

    Context::secure_json

    async fn Context::secure_json(self : Context, code : Int, data : Json) -> Unit

    Render SecureJSON — prepends while(1); to prevent JSON hijacking. See c.SecureJSON() equivalent.

    Context::set

    fn Context::set(self : Context, key : String, value : Json) -> Unit

    Store a value in the context. Useful for middleware to pass data to handlers. Example: auth middleware stores ctx.set("user_id", json).

    Context::set_accepted

    fn Context::set_accepted(self : Context, formats : Array[String]) -> Unit

    SetAccepted sets the accepted content types for content negotiation. See c.SetAccepted() equivalent.
    fn Context::set_cookie(self : Context, name : String, value : String, max_age? : Int, path? : String, domain? : String, secure? : Bool, http_only? : Bool, same_site? : String) -> Unit

    Set a cookie in the response headers.

    ctx.set_cookie("session_id", "abc123", max_age=3600, path="/", http_only=true)

    Context::set_header

    fn Context::set_header(self : Context, key : String, value : String) -> Unit

    Set a response header.

    Context::set_headers

    fn Context::set_headers(self : Context, hdrs : Map[String, String]) -> Unit

    Set multiple response headers at once.

    Context::set_params

    fn Context::set_params(self : Context, params : Map[String, String]) -> Unit

    Set parsed path parameters (called by the router after matching).

    Context::should_bind

    async fn[T :
    FromJson
    ] Context::should_bind(self : Context) -> T?

    Auto-detect the content type and bind the request body to a struct. Supports JSON (application/json) and form-encoded (application/x-www-form-urlencoded).

    Returns Some(value) on success, None on failure.

    Context::should_bind_form

    async fn[T :
    FromJson
    ] Context::should_bind_form(self : Context) -> T?

    ShouldBindForm binds form-encoded body to a struct.

    Context::should_bind_header

    fn[T :
    FromJson
    ] Context::should_bind_header(self : Context) -> T?

    ShouldBindHeader binds request headers to a struct.

    Context::should_bind_json

    async fn[T :
    FromJson
    ] Context::should_bind_json(self : Context) -> T?

    ShouldBindJSON binds the JSON request body to a struct. See c.ShouldBindJSON() equivalent.

    Context::should_bind_query

    fn[T :
    FromJson
    ] Context::should_bind_query(self : Context) -> T?

    ShouldBindQuery binds query parameters to a struct. See c.ShouldBindQuery() equivalent.

    Context::should_bind_uri

    fn[T :
    FromJson
    ] Context::should_bind_uri(self : Context) -> T?

    ShouldBindUri binds URI (path) parameters to a struct. See c.ShouldBindUri() equivalent.

    Context::should_bind_with

    async fn[T] Context::should_bind_with(self : Context, binder : async (Context) -> T?) -> T?

    ShouldBindWith binds using a custom binding function. See c.ShouldBindWith() equivalent.

    Context::size

    fn Context::size(self : Context) -> Int

    Returns the number of bytes written to the response so far. See c.Writer.Size() equivalent.

    Context::sse

    async fn Context::sse(self : Context, event : String, data : String) -> Unit

    Stream a Server-Sent Event (SSE) to the client. Sets the appropriate headers for SSE.

    // Basic SSE ctx.sse("message", "hello world")

    Context::sse_ex

    async fn Context::sse_ex(self : Context, event : String, data~ : String, id? : String, retry? : Int, comment? : String) -> Unit

    SSE with full options: id, retry, comment.

    Context::sse_flush

    async fn Context::sse_flush(self : Context) -> Unit

    Flush the SSE response — sends buffered data to the client immediately.

    Context::sse_keepalive

    async fn Context::sse_keepalive(self : Context, comment : String) -> Unit

    SSE keep-alive — sends a comment-only event to prevent connection timeout. Useful for long-lived SSE connections.

    ctx.sse_keepalive("ping")

    Context::status

    fn Context::status(self : Context, code : Int) -> Unit

    Set the HTTP status code for the response.

    Context::stream

    async fn Context::stream(self : Context, code : Int, content_type : String, writer : async (async (String) -> Unit) -> Unit) -> Unit

    Stream writes a streaming response with the given status code and content type. The writer callback receives a function that writes chunks to the connection.

    ctx.stream(200, "text/event-stream", fn(write) { for i = 0; i < 10; i = i + 1 { write("data: chunk " + i.to_string() + "\n\n") } })

    Context::stream_sse

    async fn Context::stream_sse(self : Context, event : String, data : String) -> Unit

    Backward-compatible alias for sse().

    Context::stream_start

    async fn Context::stream_start(self : Context, code : Int, content_type : String) -> Unit

    Start a chunked stream response. The response is NOT finalized, allowing multiple write calls afterwards; call end_stream to finish.

    Context::string

    async fn Context::string(self : Context, code : Int, s : String) -> Unit

    Render a plain text string.

    Context::string_f

    async fn Context::string_f(self : Context, code : Int, template : String, args : Array[String]) -> Unit

    StringF renders a formatted string response (printf-style). Supports simple {0}, {1} placeholders for variable substitution.

    ctx.string_f(200, "Hello {0}, you are {1} years old", ["Alice", "30"])

    Context::toml

    async fn Context::toml(self : Context, code : Int, obj : Json) -> Unit

    TOML renders a TOML response (placeholder — returns JSON).

    Context::write

    async fn Context::write(self : Context, data : String) -> Unit

    Write raw data to an ongoing stream response.

    Context::write_header_now

    async fn Context::write_header_now(self : Context) -> Unit

    WriteHeaderNow forces the HTTP headers to be written. See c.Writer.WriteHeaderNow() equivalent.

    Context::written

    fn Context::written(self : Context) -> Bool

    Written returns true if the response has been written. See c.Writer.Written() equivalent.

    Context::xml

    async fn Context::xml(self : Context, code : Int, data : Json, root_tag? : String) -> Unit

    Render XML. Converts a JSON object to XML format. The JSON keys become XML tag names, values become text content.

    ctx.xml(200, Json::object(H([ ("user", Json::object(H([ ("name", Json::string("Alice")), ("age", Json::string("30")), ]))), ]))) // Output: <root><user><name>Alice</name><age>30</age></user></root>

    Context::yaml

    async fn Context::yaml(self : Context, code : Int, data : Json) -> Unit

    Render YAML. Converts a JSON object to YAML format.

    ctx.yaml(200, Json::object(H([ ("name", Json::string("Alice")), ("age", Json::string("30")), ]))) // Output: // name: Alice // age: "30"

    Engine

    pub(all) struct Engine {
    router : Router
    handle_method_not_allowed : Bool
    redirect_trailing_slash : Bool
    redirect_fixed_path : Bool
    remove_extra_slash : Bool
    use_raw_path : Bool
    unescape_path_values : Bool
    max_multipart_memory : Int64
    remote_ip_headers : Array[String]
    trusted_platform : String
    forwarded_by_client_ip : Bool
    trusted_proxies : Array[String]
    active_server :
    Server
    ?
    }

    Engine is the top-level framework instance.

    Engine::any

    fn Engine::any(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a route that matches ANY HTTP method. Returns Engine for chaining.

    app.any("/api/health", [health_check])

    Engine::del

    fn Engine::del(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a DELETE route.

    Engine::get

    fn Engine::get(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a GET route. Returns Engine for chaining.

    app.get("/api/articles", [list_articles]) app.get("/api/articles/:id", [auth, get_article]) // with middleware

    Engine::group

    fn Engine::group(self : Engine, prefix : String) -> Group

    Create a route group under the given path prefix. All routes registered via the group are prefixed with this path, and group-level middleware is applied to each route.

    let api = app.group("/api") api.use(auth_middleware) api.get("/profile", [get_profile])

    Engine::handle

    fn Engine::handle(self : Engine, methods : Array[Method], pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Handle multiple HTTP methods for the same pattern.

    app.handle([GET, POST], "/api/form", [handle_form])

    Engine::handle_context

    async fn Engine::handle_context(self : Engine, ctx : Context) -> Unit

    HandleContext re-enters a context that has been rewritten. This is useful for internal redirects (modify ctx.req.path and re-dispatch).

    Engine::handle_method_not_allowed

    fn Engine::handle_method_not_allowed(self : Engine, enable : Bool) -> Unit

    Enable or disable automatic 405 Method Not Allowed responses.

    Engine::head

    fn Engine::head(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a HEAD route.

    Engine::health

    fn Engine::health(self : Engine) -> Unit

    Register a built-in /health endpoint that returns 200 OK with JSON status.

    This is useful for load balancer health checks, container probes, and monitoring systems.

    app.health()

    Engine::load_html_files

    async fn Engine::load_html_files(self : Engine, files : Array[String]) -> Unit

    Load HTML templates from a list of file paths.

    Engine::load_html_glob

    async fn Engine::load_html_glob(self : Engine, pattern : String) -> Unit

    Load HTML templates from a glob pattern (e.g., "templates/*.html").

    Engine::no_method

    fn Engine::no_method(self : Engine, handlers : Array[async (Context) -> Unit]) -> Unit

    Set a custom 405 handler. Called when a path matches but the method is not allowed.

    Engine::no_route

    fn Engine::no_route(self : Engine, handlers : Array[async (Context) -> Unit]) -> Unit

    Set a custom 404 handler. Called when no route matches.

    Engine::options

    fn Engine::options(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register an OPTIONS route.

    Engine::patch

    fn Engine::patch(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a PATCH route.

    Engine::post

    fn Engine::post(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a POST route.

    Engine::put

    fn Engine::put(self : Engine, pattern : String, handlers : Array[async (Context) -> Unit]) -> Engine

    Register a PUT route.

    Engine::redirect_fixed_path

    fn Engine::redirect_fixed_path(self : Engine, enable : Bool) -> Unit

    Enable fixed path redirect. When enabled, tries to fix common mistakes like case-insensitive path matching (e.g., /Users → /users). Default is false.

    Engine::redirect_trailing_slash

    fn Engine::redirect_trailing_slash(self : Engine, enable : Bool) -> Unit

    Enable automatic trailing slash redirect. When enabled, /path redirects to /path/ and vice versa.

    Engine::remove_extra_slash

    fn Engine::remove_extra_slash(self : Engine, enable : Bool) -> Unit

    Enable removal of extra slashes from paths. When enabled, //api//v1//users is cleaned to /api/v1/users. Default is false.

    Engine::routes

    fn Engine::routes(self : Engine) -> Array[RouteInfo]

    List all registered routes.

    for route in app.routes() { println("\{route.method} \{route.path}") }

    Engine::run

    async fn Engine::run(self : Engine, addr : String) -> Unit

    Start the HTTP server on the given address (e.g., "0.0.0.0:8080"). This is a blocking call — it runs forever until the process is killed.

    Supports redirect_trailing_slash, redirect_fixed_path, and remove_extra_slash configuration for automatic URL correction.

    app.run("127.0.0.1:3000")

    Engine::run_default

    async fn Engine::run_default(self : Engine) -> Unit

    Start the server on the default address "0.0.0.0:8080". Equivalent to app.run("0.0.0.0:8080").

    app.run_default()

    Engine::run_fd

    fn Engine::run_fd(self : Engine, fd : Int) -> Unit

    RunFd starts an HTTP server on a given file descriptor. Useful for systemd socket activation or inherited sockets.

    app.run_fd(3) // Listen on fd 3 passed by systemd

    Engine::run_listener

    RunListener starts the server with a custom request handler. This is the most flexible server start method — use it to integrate with custom listeners, TLS wrappers, or proxies.

    app.run_listener(fn(request, reader, conn) { // custom pre-processing app.router.dispatch(request, reader, conn) })

    Engine::run_tls

    async fn Engine::run_tls(self : Engine, addr : String, cert_file : String, key_file : String) -> Unit

    RunTLS starts an HTTPS server with TLS encryption. Reads certificate and key from the given file paths.

    Uses the built-in TLS support from @tls.Tls when available.

    app.run_tls("0.0.0.0:443", "./cert.pem", "./key.pem")

    Engine::run_tls_with_config

    async fn Engine::run_tls_with_config(self : Engine, addr : String, tls_config : TLSConfig) -> Unit

    RunTLSWithConfig starts an HTTPS server with full TLS configuration. Supports cert auto-reload, TLS version selection, and cipher suite config.

    let tls_config = TLSConfig::from_files("./cert.pem", "./key.pem") |> with_auto_reload(interval=1800) app.run_tls_with_config("0.0.0.0:443", tls_config)

    Engine::run_unix

    fn Engine::run_unix(self : Engine, file : String) -> Unit

    RunUnix starts an HTTP server on a Unix domain socket. Unix sockets are useful for local IPC without TCP overhead.

    Note: Requires platform support (Linux, macOS). On unsupported platforms, prints a warning and returns.

    app.run_unix("/tmp/mbit.sock")

    Engine::run_with_404

    async fn Engine::run_with_404(self : Engine, addr : String, not_found_handler : async (Context) -> Unit) -> Unit

    Run the server with a custom 404 handler (convenience wrapper). This is equivalent to:
    app.no_route([not_found_handler]) app.run(addr)

    Engine::run_with_shutdown

    async fn Engine::run_with_shutdown(self : Engine, addr : String, on_shutdown? : () -> Unit) -> Unit

    RunWithShutdown starts the server with graceful shutdown support. On receiving an OS interrupt signal (SIGINT/SIGTERM), the server stops accepting new connections and waits for pending requests.

    The optional on_shutdown callback is invoked when shutdown begins.

    Note: Full signal handling requires platform-native OS support. For now, use app.shutdown() from a health-check endpoint.

    app.run_with_shutdown("0.0.0.0:8080")

    Engine::serve_http

    ServeHTTP makes Engine implement the HTTP handler interface. This allows using the engine with custom HTTP servers.

    Engine::set_forwarded_by_client_ip

    fn Engine::set_forwarded_by_client_ip(self : Engine, enable : Bool) -> Unit

    Enable/disable trusting the X-Forwarded-For header.

    Engine::set_func_map

    fn Engine::set_func_map(self : Engine, funcs : Map[String, String]) -> Unit

    SetFuncMap sets a simple template function map (key → replacement value). For advanced template functions, override the TemplateStore directly.

    Engine::set_html_template

    fn Engine::set_html_template(self : Engine, name : String, content : String) -> Unit

    Set a single HTML template by name and content string.

    Engine::set_max_multipart_memory

    fn Engine::set_max_multipart_memory(self : Engine, bytes : Int64) -> Unit

    Set the maximum memory for multipart form parsing. Default is 32 MB.

    Engine::set_remote_ip_headers

    fn Engine::set_remote_ip_headers(self : Engine, headers : Array[String]) -> Unit

    Set the headers to check for client IP. Default: ["X-Forwarded-For", "X-Real-IP"]

    Engine::set_template_delims

    fn Engine::set_template_delims(self : Engine, left : String, right : String) -> Unit

    Set custom template delimiters (default: {{ and }}).

    Engine::set_trusted_platform

    fn Engine::set_trusted_platform(self : Engine, platform : String) -> Unit

    Set a trusted platform header for determining client IP. e.g., "X-Appengine-Remote-Addr", "CF-Connecting-IP"

    Engine::set_trusted_proxies

    fn Engine::set_trusted_proxies(self : Engine, proxies : Array[String]) -> Unit

    Set trusted proxy CIDRs (e.g., ["10.0.0.0/8", "172.16.0.0/12"]).

    Engine::set_unescape_path_values

    fn Engine::set_unescape_path_values(self : Engine, enable : Bool) -> Unit

    Unescape path values (percent-decode URL paths). Default is true.

    Engine::shutdown

    fn Engine::shutdown(self : Engine, cleanup? : () -> Unit) -> Bool

    Shutdown initiates a graceful shutdown of the server. This can be called from a signal handler or health check endpoint.

    Returns true if the server was running and is now shutting down.

    // In a health check or admin endpoint: app.shutdown(cleanup=fn() { println("Cleaning up resources...") })

    Engine::static_file

    fn Engine::static_file(self : Engine, relative_path : String, file_path : String) -> Unit

    Serve a single static file at the given path.

    app.static_file("/favicon.ico", "./assets/favicon.ico")

    Engine::static_files_engine

    fn Engine::static_files_engine(self : Engine, relative_path : String, root : String) -> Unit

    Serve static files from a directory under the given URL prefix.

    app.static_files_engine("/assets", "./public")

    Engine::static_fs

    fn Engine::static_fs(self : Engine, relative_path : String, root : String) -> Unit

    Serve static files from a file system (alias for static_files_engine).

    Engine::unescape_path_values

    fn Engine::unescape_path_values(self : Engine, enable : Bool) -> Unit

    Unescape percent-encoded path values (e.g., %20 → space). Default is true.

    Engine::use

    fn Engine::use(self : Engine, handler : async (Context) -> Unit) -> Engine

    Add global middleware. Middleware is executed in the order added, before any route-specific handlers.

    app.use(@mbit.logger()) app.use(@mbit.cors(@mbit.CORSConfig::default()))

    Engine::use_escaped_path

    fn Engine::use_escaped_path(self : Engine, enable : Bool) -> Unit

    Enable escaped path support — percent-encoded paths are decoded. Default is false.

    Engine::use_h2c

    fn Engine::use_h2c(self : Engine, _enable : Bool) -> Unit

    Enable HTTP/2 cleartext (h2c) support. Note: Requires platform and library support.

    Engine::use_raw_path

    fn Engine::use_raw_path(self : Engine, enable : Bool) -> Unit

    Use the raw (percent-encoded) URL path instead of the decoded one. Default is false.

    Engine::with_options

    fn Engine::with_options(self : Engine, opts : Array[(Engine) -> Unit]) -> Engine

    Returns a new Engine with the given option functions applied.

    FieldRules

    pub(all) struct FieldRules {
    field : String
    rules : Array[Rule]
    }

    Field validation rules — maps field name to an array of rules. Example:
    let rules = [FieldRules("username", [Required, Min(3), Max(20)]), FieldRules("email", [Required, Email]), FieldRules("age", [Min(0), Max(150)])]

    FieldRules::new

    fn FieldRules::new(field : String, rules : Array[Rule]) -> FieldRules

    Create field rules.

    FileHeader

    pub(all) struct FileHeader {
    field_name : String
    filename : String
    content : String
    content_type : String
    size : Int64
    }

    Structured metadata for an uploaded file.

    Group

    pub(all) struct Group {
    router : Router
    prefix : String
    middlewares : Array[async (Context) -> Unit]
    }

    A route group shares a path prefix and middleware.

    Group::any

    fn Group::any(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a route that matches ANY HTTP method under this group.

    group.any("/health", [health_check])

    Group::base_path

    fn Group::base_path(self : Group) -> String

    Return the base path of this group.

    let api = app.group("/api/v1") println(api.base_path()) // "/api/v1"

    Group::del

    fn Group::del(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a DELETE route under this group.

    Group::get

    fn Group::get(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a GET route under this group.

    Group::group

    fn Group::group(self : Group, prefix : String) -> Group

    Create a nested group under this group. The nested group inherits this group's prefix and middleware.

    Group::handle

    fn Group::handle(self : Group, methods : Array[Method], pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Handle multiple HTTP methods for the same pattern under this group.

    Group::handlers

    fn Group::handlers(self : Group) -> Array[async (Context) -> Unit]

    Return the middleware handlers attached to this group. See group.Handlers equivalent.

    Group::head

    fn Group::head(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a HEAD route under this group.

    Group::new

    fn Group::new(router : Router, prefix : String) -> Group

    Create a new route group.

    Group::options

    fn Group::options(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register an OPTIONS route under this group.

    Group::patch

    fn Group::patch(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a PATCH route under this group.

    Group::post

    fn Group::post(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a POST route under this group.

    Group::put

    fn Group::put(self : Group, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a PUT route under this group.

    Group::static_file

    fn Group::static_file(self : Group, relative_path : String, file_path : String) -> Unit

    Serve a single static file under this group's prefix.

    let assets = app.group("/assets") assets.static_file("/favicon.ico", "./public/favicon.ico")

    Group::static_files

    fn Group::static_files(self : Group, relative_path : String, root : String) -> Unit

    Serve static files from a directory under this group's prefix.

    let assets = app.group("/assets") assets.static("/css", "./public/css")

    Group::static_fs

    fn Group::static_fs(self : Group, relative_path : String, root : String) -> Unit

    Serve static files from a file system under this group's prefix. See group.StaticFS() equivalent.

    let assets = app.group("/assets") assets.static_fs("/css", "./public/css")

    Group::use

    fn Group::use(self : Group, handler : async (Context) -> Unit) -> Group

    Add middleware to this group. Middleware is applied to all routes registered through this group, in the order added. Returns the group for method chaining.

    GzipConfig

    pub(all) struct GzipConfig {
    min_length : Int
    level : Int
    compressible_types : Array[String]
    excluded_types : Array[String]
    }

    Gzip compression configuration.

    GzipConfig::default

    fn GzipConfig::default() -> GzipConfig

    Default gzip configuration.

    LogFormat

    pub(all) enum LogFormat {
    TextFormat
    JSONFormat
    } derive(Eq,
    Debug
    )

    Log output format.

    LogLevel

    pub(all) enum LogLevel {
    Debug
    Info
    Warn
    Error
    Fatal
    } derive(Compare, Eq, Hash,
    Debug
    )

    Log severity levels — maps to @leppard/moonbit-log.Level.

    LogLevel::to_string

    fn LogLevel::to_string(self : LogLevel) -> String

    Convert LogLevel to string.

    Logger

    pub(all) struct Logger {
    }

    Logger — the central logging API for mbit.

    All logging goes through moonbit-log (leppard/moonbit-log) and respects configured log levels and output formats.

    Logger::debug

    fn Logger::debug(message : String, fields? : Array[(String, String)]) -> Unit

    Log a debug message with optional key-value fields.

    Logger::debug("Cache miss", fields=[("key", "user:42")])

    Logger::error

    fn Logger::error(message : String, fields? : Array[(String, String)]) -> Unit

    Log an error message with optional key-value fields.

    Logger::fatal

    fn Logger::fatal(message : String, fields? : Array[(String, String)]) -> Unit

    Log a fatal message and terminate.

    Logger::info

    fn Logger::info(message : String, fields? : Array[(String, String)]) -> Unit

    Log an info message with optional key-value fields.

    Logger::is_enabled

    fn Logger::is_enabled(level : LogLevel) -> Bool

    Check if the given log level is enabled.

    Logger::level

    fn Logger::level() -> LogLevel

    Get the current log level.

    Logger::set_app_name

    fn Logger::set_app_name(name : String) -> Unit

    Set the application name for log metadata. Injected as a field into every log entry.

    Logger::set_environment

    fn Logger::set_environment(env : String) -> Unit

    Set the environment tag for log metadata. Injected as a field ("env") into every log entry.

    Logger::set_format

    fn Logger::set_format(format : LogFormat) -> Unit

    Set the log output format.
    • TextFormat: human-readable compact format
    • JSONFormat: JSON lines format

    Logger::set_level

    fn Logger::set_level(level : LogLevel) -> Unit

    Set the minimum log level.

    Logger::warn

    fn Logger::warn(message : String, fields? : Array[(String, String)]) -> Unit

    Log a warning message with optional key-value fields.

    MatchResult

    pub(all) struct MatchResult {
    handlers : Array[async (Context) -> Unit]
    params : Map[String, String]
    node_middlewares : Array[async (Context) -> Unit]
    }

    Result of route matching.

    Mbit

    pub struct Mbit {
    engine : Engine
    }

    Mbit wraps an Engine providing method-based access.

    Mbit::any

    fn Mbit::any(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::default

    fn Mbit::default() -> Mbit

    Create an Mbit with default settings (logger + recovery middleware).

    Mbit::delete

    fn Mbit::delete(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::engine

    fn Mbit::engine(self : Mbit) -> Engine

    Expose the underlying Engine for advanced usage.

    Mbit::get

    fn Mbit::get(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit
    ——————————————————————————————————————————————————————————————————————

    Mbit::group

    fn Mbit::group(self : Mbit, prefix : String) -> Group
    ——————————————————————————————————————————————————————————————————————

    Mbit::handle

    fn Mbit::handle(self : Mbit, methods : Array[Method], pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::handle_method_not_allowed

    fn Mbit::handle_method_not_allowed(self : Mbit, enable : Bool) -> Unit

    Mbit::head

    fn Mbit::head(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::health

    fn Mbit::health(self : Mbit) -> Unit

    Register a built-in /health endpoint.

    Mbit::max_multipart_memory

    fn Mbit::max_multipart_memory(self : Mbit, bytes : Int64) -> Unit
    ——————————————————————————————————————————————————————————————————————

    Mbit::new

    fn Mbit::new(engine : Engine) -> Mbit

    Create an Mbit with a custom engine.

    Mbit::no_method

    fn Mbit::no_method(self : Mbit, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::no_route

    fn Mbit::no_route(self : Mbit, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::options

    fn Mbit::options(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::patch

    fn Mbit::patch(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::post

    fn Mbit::post(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::put

    fn Mbit::put(self : Mbit, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Mbit::redirect_fixed_path

    fn Mbit::redirect_fixed_path(self : Mbit, enable : Bool) -> Unit

    Mbit::redirect_trailing_slash

    fn Mbit::redirect_trailing_slash(self : Mbit, enable : Bool) -> Unit

    Mbit::remove_extra_slash

    fn Mbit::remove_extra_slash(self : Mbit, enable : Bool) -> Unit

    Mbit::routes

    fn Mbit::routes(self : Mbit) -> Array[RouteInfo]
    ——————————————————————————————————————————————————————————————————————

    Mbit::run

    async fn Mbit::run(self : Mbit, addr : String) -> Unit
    ——————————————————————————————————————————————————————————————————————

    Mbit::run_default

    async fn Mbit::run_default(self : Mbit) -> Unit

    Mbit::run_with_shutdown

    async fn Mbit::run_with_shutdown(self : Mbit, addr : String, on_shutdown? : () -> Unit) -> Unit

    Mbit::set_func_map

    fn Mbit::set_func_map(self : Mbit, funcs : Map[String, String]) -> Unit

    Mbit::set_html_template

    fn Mbit::set_html_template(self : Mbit, name : String, content : String) -> Unit
    ——————————————————————————————————————————————————————————————————————

    Mbit::set_template_delims

    fn Mbit::set_template_delims(self : Mbit, left : String, right : String) -> Unit

    Mbit::shutdown

    fn Mbit::shutdown(self : Mbit, cleanup? : () -> Unit) -> Bool

    Mbit::static_

    fn Mbit::static_(self : Mbit, relative_path : String, root : String) -> Unit

    Mbit::static_file

    fn Mbit::static_file(self : Mbit, relative_path : String, file_path : String) -> Unit
    ——————————————————————————————————————————————————————————————————————

    Mbit::static_fs

    fn Mbit::static_fs(self : Mbit, relative_path : String, root : String) -> Unit

    Mbit::trees

    fn Mbit::trees(self : Mbit) -> String

    Mbit::use

    fn Mbit::use(self : Mbit, handler : async (Context) -> Unit) -> Unit
    ——————————————————————————————————————————————————————————————————————

    MbitError

    pub(all) enum MbitError {
    BindError(String)
    ValidationError(String)
    NotFound(String)
    InternalError(String)
    } derive(
    Debug
    )

    Framework-level error for binding / validation

    Method

    pub(all) enum Method {
    GET
    POST
    PUT
    DELETE
    PATCH
    HEAD
    OPTIONS
    } derive(Eq, Hash,
    Debug
    )

    HTTP method enumeration

    Method::from_native

    Convert from native HTTP RequestMethod

    Method::to_native

    Convert to native HTTP RequestMethod

    Method::to_string

    fn Method::to_string(self : Method) -> String

    Convert Method to string

    MetricsCollector

    pub(all) struct MetricsCollector {
    status_counts : Map[Int, Int64]
    path_counts : Map[String, Int64]
    total_requests : Int64
    total_latency_ms : Int64
    min_latency_ms : Int64
    max_latency_ms : Int64
    }

    A simple in-memory metrics collector for request counts and latencies.

    MetricsCollector::new

    Create a new metrics collector.

    MetricsCollector::record

    fn MetricsCollector::record(self : MetricsCollector, status : Int, path : String, latency_ms : Int64) -> Unit

    Record a request in the metrics collector.

    MetricsCollector::to_json

    fn MetricsCollector::to_json(self : MetricsCollector) -> Json

    Export metrics as JSON for a /metrics endpoint.

    Mode

    pub(all) enum Mode {
    Debug
    Release
    Test
    } derive(Eq,
    Debug
    )

    Framework running mode.

    Mode::to_string

    fn Mode::to_string(self : Mode) -> String

    Convert mode to a display string.

    MultipartPartReader

    pub(all) struct MultipartPartReader {
    field_name : String
    filename : String
    content_type : String
    remaining : Int64
    pos : Int
    body : String
    boundary_marker : String
    done : Bool
    }

    A single part in a streaming multipart parse, with header info and a reader for chunked body access.

    MultipartPartReader::drain

    fn MultipartPartReader::drain(self : MultipartPartReader) -> String

    Convenience: drain a multipart part completely into a string.

    MultipartPartReader::read_chunk

    fn MultipartPartReader::read_chunk(self : MultipartPartReader, max_size : Int) -> String?

    Read the next chunk of the current multipart part body. Returns Some(chunk) with data, or None when the part is complete.

    let stream = MultipartStream::new(ctx) match stream.next_part() { Some(part) => { loop { match part.read_chunk(4096) { Some(chunk) => { /* process chunk */ } None => break } } } None => {} }

    MultipartStream

    pub(all) struct MultipartStream {
    body : String
    boundary : String
    pos : Int
    done : Bool
    }

    Streaming multipart parser state.

    MultipartStream::new

    Create a streaming multipart parser from a Context.

    The parser reads the request body into a buffer and provides incremental access to each multipart part.

    MultipartStream::next_part

    Read the next available part from the multipart stream. Returns None when all parts have been consumed.

    The returned MultipartPartReader lets you read the part body chunk by chunk via read_chunk().

    Node

    pub(all) struct Node {
    seg : String
    kind : NodeKind
    children : Map[String, Node]
    param_child : Node?
    wildcard_child : Node?
    handlers : Array[async (Context) -> Unit]
    middlewares : Array[async (Context) -> Unit]
    }

    A node in the route trie.

    NodeKind

    type NodeKind

    RateLimitConfig

    pub(all) struct RateLimitConfig {
    max_requests : Int
    window_secs : Int
    strategy : RateLimitStrategy
    token_bucket : RateTokenBucket
    key_fn : (Context) -> String
    condition : (Context) -> Bool
    include_headers : Bool
    }

    Rate limit configuration.

    RateLimitConfig::default

    Default rate limit: 100 requests per minute (fixed window).

    RateLimitConfig::token_bucket

    fn RateLimitConfig::token_bucket(bucket : RateTokenBucket) -> RateLimitConfig

    Create a token bucket rate limit config.

    RateLimitConfig::when

    fn RateLimitConfig::when(self : RateLimitConfig, condition : (Context) -> Bool) -> RateLimitConfig

    Set a condition for when rate limiting applies.

    RateLimitConfig::with_key

    fn RateLimitConfig::with_key(self : RateLimitConfig, key_fn : (Context) -> String) -> RateLimitConfig

    Set a custom key extractor (e.g., per-user or per-route).

    RateLimitStrategy

    pub(all) enum RateLimitStrategy {
    FixedWindow
    SlidingWindowLog
    TokenBucket
    } derive(Eq,
    Debug
    )

    Rate limiter algorithm strategy.

    RateTokenBucket

    pub(all) struct RateTokenBucket {
    rate : Double
    burst : Int
    }

    Token bucket configuration.

    RenderType

    pub(all) enum RenderType {
    JSON
    IndentedJSON
    SecureJSON
    AsciiJSON
    PureJSON
    JSONP
    XML
    YAML
    TOML
    ProtoBuf
    } derive(Eq,
    Debug
    )

    Render type enumeration for the Render() method.

    ResponseConn

    pub(all) enum ResponseConn {
    Real(
    ServerConnection
    )
    Test(TestConn)
    }

    The response sink — either a real HTTP server connection or an in-memory buffer used for testing.

    ResponseConn::end_response

    async fn ResponseConn::end_response(self : ResponseConn) -> Unit

    Finish the response.

    ResponseConn::flush

    async fn ResponseConn::flush(self : ResponseConn) -> Unit

    Flush buffered data to the client.

    ResponseConn::send_response

    async fn ResponseConn::send_response(self : ResponseConn, code : Int, reason : String, extra_headers? : Map[String, String]) -> Unit

    Send the response status line and headers.

    ResponseConn::write_body

    async fn ResponseConn::write_body(self : ResponseConn, s : String) -> Unit

    Write a chunk of the response body.

    ResponseWriter

    pub(all) struct ResponseWriter {
    conn : ResponseConn
    size : Int
    status : Int
    written : Bool
    }

    ResponseWriter wraps the response connection with size tracking.

    ResponseWriter::end

    async fn ResponseWriter::end(self : ResponseWriter) -> Unit

    EndResponse signals the end of the response.

    ResponseWriter::flush

    async fn ResponseWriter::flush(self : ResponseWriter) -> Unit

    Flush any buffered data to the client.

    ResponseWriter::new

    ResponseWriter::size

    fn ResponseWriter::size(self : ResponseWriter) -> Int

    Size returns the number of bytes written.

    ResponseWriter::status

    fn ResponseWriter::status(self : ResponseWriter) -> Int

    Status returns the HTTP status code.

    ResponseWriter::write

    async fn ResponseWriter::write(self : ResponseWriter, data : String) -> Unit

    Write data to the response body.

    ResponseWriter::write_header

    async fn ResponseWriter::write_header(self : ResponseWriter, code : Int) -> Unit

    WriteHeader sends the HTTP response header with the given status code.

    ResponseWriter::write_header_now

    async fn ResponseWriter::write_header_now(self : ResponseWriter) -> Unit

    WriteHeaderNow forces the response header to be written immediately.

    ResponseWriter::write_string

    async fn ResponseWriter::write_string(self : ResponseWriter, s : String) -> Unit

    WriteString writes a string to the response.

    ResponseWriter::written

    fn ResponseWriter::written(self : ResponseWriter) -> Bool

    Written returns true if the header has been written.

    RouteInfo

    pub(all) struct RouteInfo {
    meth : String
    path : String
    handler_count : Int
    }

    Information about a single registered route.

    Router

    pub(all) struct Router {
    roots : Map[Method, Node]
    global_middleware : Array[async (Context) -> Unit]
    no_route_handlers : Array[async (Context) -> Unit]
    no_method_handlers : Array[async (Context) -> Unit]
    }

    The Router stores all routes in a trie and dispatches incoming requests.

    Router::any

    fn Router::any(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a route that matches ANY HTTP method.

    router.any("/api/health", [health_check])

    Router::del

    fn Router::del(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a DELETE route.

    Router::dispatch

    Dispatch an incoming native HTTP request through the router. Returns true if a matching route was found and handlers were executed, false otherwise (caller should send a fallback 404).

    Router::get

    fn Router::get(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a GET route with one or more handlers. The last handler is typically the "real" handler; preceding ones are middleware.

    Router::handle

    fn Router::handle(self : Router, methods : Array[Method], pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Handle multiple HTTP methods for the same pattern.

    Router::head

    fn Router::head(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a HEAD route.

    Router::new

    fn Router::new() -> Router

    Create a new empty Router.

    Router::no_method

    fn Router::no_method(self : Router, handlers : Array[async (Context) -> Unit]) -> Unit

    Set a custom 405 handler. Called when a path matches but the HTTP method is not allowed.

    Router::no_route

    fn Router::no_route(self : Router, handlers : Array[async (Context) -> Unit]) -> Unit

    Set a custom 404 handler. Called when no route matches the request path.

    Router::options

    fn Router::options(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register an OPTIONS route.

    Router::patch

    fn Router::patch(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a PATCH route.

    Router::post

    fn Router::post(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a POST route.

    Router::put

    fn Router::put(self : Router, pattern : String, handlers : Array[async (Context) -> Unit]) -> Unit

    Register a PUT route.

    Router::routes

    fn Router::routes(self : Router) -> Array[RouteInfo]

    List all registered routes. Returns an array of (method, path) pairs.

    let routes = router.routes() for route in routes { println("\{route.method} \{route.path}") }

    Router::trees

    fn Router::trees(self : Router) -> String

    Trees returns a human-readable string representation of all routing trees. Prints routing tree for debugging.

    Router::use

    fn Router::use(self : Router, handler : async (Context) -> Unit) -> Unit

    Add global middleware (applied to every route).

    Router::use_on

    fn Router::use_on(self : Router, meth : Method, pattern : String, handler : async (Context) -> Unit) -> Unit

    Add middleware to a specific route pattern (applies to that subtree).

    Rule

    pub(all) enum Rule {
    Required
    Min(Int)
    Max(Int)
    Len(Int)
    Pattern(String)
    OneOf(Array[String])
    Email
    URL
    Custom((String) -> String?)
    }

    A single validation rule for a field.

    SecureConfig

    pub(all) struct SecureConfig {
    frame_options : String
    content_type_nosniff : Bool
    xss_protection : Bool
    content_security_policy : String
    referrer_policy : String
    strict_transport_security : String
    permissions_policy : String
    cross_origin_opener_policy : String
    is_api : Bool
    skip_hsts : Bool
    enable_csrf : Bool
    csrf_secret : String
    csrf_cookie_name : String
    csrf_header_name : String
    csrf_form_name : String
    safe_methods : Array[String]
    csrf_exclude_paths : Array[String]
    csrf_token_expiry : Int
    }

    Secure headers configuration.

    SecureConfig::api_default

    fn SecureConfig::api_default() -> SecureConfig

    Default secure configuration for APIs (no frame options, no CSP).

    SecureConfig::default

    fn SecureConfig::default() -> SecureConfig

    Default secure configuration — balanced security for web applications.

    SecureConfig::with_csrf

    fn SecureConfig::with_csrf(self : SecureConfig) -> SecureConfig

    Enable CSRF protection on this config.

    SecureConfig::with_csrf_exclude

    fn SecureConfig::with_csrf_exclude(self : SecureConfig, paths : Array[String]) -> SecureConfig

    Add paths to exclude from CSRF protection.

    SecureConfig::with_csrf_secret

    fn SecureConfig::with_csrf_secret(self : SecureConfig, secret : String) -> SecureConfig

    Set a custom CSRF secret.

    ServerConfig

    pub(all) struct ServerConfig {
    host : String
    port : Int
    database_url : String
    redis_url : String
    log_level : String
    log_format : String
    secret_key : String
    cors_origins : Array[String]
    max_upload_size : Int64
    rate_limit : Int
    rate_limit_window : Int
    tls_cert_file : String
    tls_key_file : String
    environment : AppEnvironment
    debug : Bool
    }

    Strongly-typed server configuration populated from environment variables.

    ServerConfig::addr

    fn ServerConfig::addr(self : ServerConfig) -> String

    Get the server address string (host:port).

    ServerConfig::from_env

    fn ServerConfig::from_env() -> ServerConfig

    Load server config from environment variables.

    ServerConfig::has_tls

    fn ServerConfig::has_tls(self : ServerConfig) -> Bool

    Whether TLS is configured (cert and key files are set).

    StructuredLogConfig

    pub(all) struct StructuredLogConfig {
    level : LogLevel
    log_request_body : Bool
    log_response_body : Bool
    max_body_log_length : Int
    skip_paths : Array[String]
    include_latency : Bool
    static_fields : Array[(String, String)]
    }

    Configuration for the structured request logger middleware.

    StructuredLogConfig::default

    Default structured log config.

    TLSConfig

    pub(all) struct TLSConfig {
    cert_file : String
    key_file : String
    min_version : String
    auto_reload : Bool
    reload_interval : Int
    cipher_suites : Array[String]
    }

    TLS configuration for HTTPS servers.

    TLSConfig::default

    fn TLSConfig::default() -> TLSConfig

    Default TLS configuration.

    TLSConfig::from_files

    fn TLSConfig::from_files(cert_file : String, key_file : String) -> TLSConfig

    Create a TLS config from cert and key file paths.

    TLSConfig::with_auto_reload

    fn TLSConfig::with_auto_reload(self : TLSConfig, interval? : Int) -> TLSConfig

    Enable automatic certificate reloading.

    TemplateStore

    pub(all) struct TemplateStore {
    templates : Map[String, String]
    delims_left : String
    delims_right : String
    }

    Template store — maps template name to template string.

    TemplateStore::add_template

    fn TemplateStore::add_template(self : TemplateStore, name : String, content : String) -> Unit

    Load a single template from a string.

    TemplateStore::load_files

    async fn TemplateStore::load_files(self : TemplateStore, files : Array[String]) -> Unit

    Load templates from a list of file paths.

    TemplateStore::load_glob

    async fn TemplateStore::load_glob(self : TemplateStore, pattern : String) -> Unit

    Load templates from a glob pattern. Reads all matching files.

    TemplateStore::new

    TemplateStore::render

    fn TemplateStore::render(self : TemplateStore, name : String, data : Map[String, String]) -> String?

    Render a template with data. Replaces {{ .key }} placeholders.

    TemplateStore::set_delims

    fn TemplateStore::set_delims(self : TemplateStore, left : String, right : String) -> Unit

    Set template delimiters (default: {{ and }}).

    TestConn

    pub(all) struct TestConn {
    status : Int
    reason : String
    headers : Map[String, String]
    body : String
    header_sent : Bool
    ended : Bool
    }

    In-memory connection used by tests — captures status, headers, and body.

    TestConn::new

    fn TestConn::new() -> TestConn

    Create a fresh in-memory test connection.

    UploadProgress

    pub(all) struct UploadProgress {
    total_size : Int64
    bytes_processed : Int64
    percentage : Double
    current_chunk : Int
    total_chunks : Int
    }

    Progress info for large file uploads.

    UploadProgress::new

    fn UploadProgress::new(total_size : Int64, chunk_size : Int) -> UploadProgress

    Create a progress tracker for large uploads.

    UploadProgress::update

    fn UploadProgress::update(self : UploadProgress, chunk_bytes : Int64) -> UploadProgress

    Update progress after processing a chunk.

    ValidationError

    pub(all) struct ValidationError {
    field : String
    message : String
    } derive(
    Debug
    )

    A validation error for a single field.

    ValidationError::to_string

    fn ValidationError::to_string(self : ValidationError) -> String

    Error string for a ValidationError.

    ValidationErrors

    pub(all) struct ValidationErrors {
    errors : Array[ValidationError]
    }

    Collection of validation errors.

    ValidationErrors::add

    fn ValidationErrors::add(self : ValidationErrors, field : String, message : String) -> Unit

    Add a validation error.

    ValidationErrors::all

    All validation errors.

    ValidationErrors::first_error

    fn ValidationErrors::first_error(self : ValidationErrors) -> String?

    Get the first error message, if any.

    ValidationErrors::has_errors

    fn ValidationErrors::has_errors(self : ValidationErrors) -> Bool

    Whether there are any errors.

    ValidationErrors::new

    Create a new empty ValidationErrors.

    ValidationErrors::to_json

    fn ValidationErrors::to_json(self : ValidationErrors) -> Json

    Convert validation errors to a JSON response body.

    WebSocket

    pub(all) struct WebSocket {
    conn :
    ServerConnection

    reader : &
    Reader

    closed : Bool
    }

    WebSocket connection state.

    WebSocket::close

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

    Close the WebSocket connection.

    WebSocket::is_closed

    fn WebSocket::is_closed(self : WebSocket) -> Bool

    Check if the WebSocket connection is closed.

    WebSocket::read

    async fn WebSocket::read(self : WebSocket) -> WebSocketMessage?

    Read the next message from the WebSocket connection. Returns None if the connection is closed or an error occurs.

    WebSocket::send_binary

    async fn WebSocket::send_binary(self : WebSocket, data : String) -> Unit

    Send a binary message over the WebSocket connection.

    WebSocket::send_close

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

    Send a close frame with optional code and reason.

    WebSocket::send_ping

    async fn WebSocket::send_ping(self : WebSocket, data : String) -> Unit

    Send a ping frame.

    WebSocket::send_pong

    async fn WebSocket::send_pong(self : WebSocket, data : String) -> Unit

    Send a pong frame.

    WebSocket::send_text

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

    Send a text message over the WebSocket connection.

    WebSocketMessage

    pub(all) enum WebSocketMessage {
    Text(String)
    Binary(String)
    Close(Int, String)
    Ping(String)
    Pong(String)
    } derive(
    Debug
    )

    A parsed WebSocket message.

    WebSocketOpcode

    pub(all) enum WebSocketOpcode {
    Continuation
    Text
    Binary
    Close
    Ping
    Pong
    } derive(Eq,
    Debug
    )

    WebSocket opcodes as defined in RFC 6455.

    basic_auth

    fn basic_auth(config : BasicAuthConfig) -> (async (Context) -> Unit)

    Basic authentication middleware.

    basic_auth_for_realm

    fn basic_auth_for_realm(accounts : Map[String, String], realm : String) -> (async (Context) -> Unit)

    BasicAuthForRealm is a convenience wrapper that creates a basic auth middleware with just an accounts map and realm string.

    Convenience for basic auth with a custom realm.

    let accounts = Map([("admin", "secret")]) app.use(basic_auth_for_realm(accounts, "My Realm"))

    bind

    async fn[T :
    FromJson
    ] bind(ctx : Context) -> T?

    Try to bind from JSON body first, then fall back to query string.

    bind_form

    async fn[T :
    FromJson
    ] bind_form(ctx : Context) -> T?

    Bind form-encoded body to a struct that implements from_json. Returns Some(value) on success, None on parse failure.

    struct ContactForm { name : String email : String message : String } derive(FromJson) let form : ContactForm? = bind_form(ctx)

    bind_header

    fn[T :
    FromJson
    ] bind_header(ctx : Context) -> T?

    Bind request headers to a struct that implements from_json. Header names are normalized to lowercase.

    struct Headers { content_type : String authorization : String } derive(FromJson) let hdrs : Headers? = bind_header(ctx)

    bind_json

    async fn[T :
    FromJson
    ] bind_json(ctx : Context) -> T?

    Bind JSON body to a struct that implements from_json. Returns Some(value) on success, None on parse failure.

    struct LoginRequest { username : String password : String } derive(FromJson) let req : LoginRequest? = bind_json(ctx)

    bind_path

    fn[T :
    FromJson
    ] bind_path(ctx : Context) -> T?

    Bind path parameters to a struct that implements from_json.

    struct ArticleParams { id : String slug : String } derive(FromJson) // Route: /article/:id/:slug let params : ArticleParams? = bind_path(ctx)

    bind_query

    fn[T :
    FromJson
    ] bind_query(ctx : Context) -> T?

    Bind query parameters to a struct that implements from_json. Converts query string to a JSON object first.

    bind_uri

    fn[T :
    FromJson
    ] bind_uri(ctx : Context) -> T?

    Bind URI (path) parameters — alias for bind_path.

    body_size_limit

    fn body_size_limit(max_bytes : Int64) -> (async (Context) -> Unit)

    Body size limiter middleware — aborts with 413 if Content-Length exceeds limit.

    Usage:
    app.use(body_size_limit(1024 * 1024)) // 1 MB

    clean_path

    fn clean_path(path : String) -> String

    Clean a URL path by removing duplicate slashes and resolving ".." and ".".

    cors

    fn cors(config : CORSConfig) -> (async (Context) -> Unit)

    CORS middleware with custom configuration.

    create_test_context

    fn create_test_context(meth : String, path : String) -> Context

    CreateTestContext returns a Context suitable for testing. Uses a minimal dummy HTTP request and an in-memory test connection.

    let ctx = create_test_context("GET", "/test") assert_eq(ctx.method(), "GET")

    create_test_context_with_handlers

    fn create_test_context_with_handlers(meth : String, path : String, handlers : Array[async (Context) -> Unit]) -> Context

    Get a test context with pre-set handlers for chain testing.

    current_environment

    fn current_environment() -> AppEnvironment

    Get the current environment from APP_ENV or MOONBIT_ENV.

    debug_print

    fn debug_print(msg : String) -> Unit

    Debug print with format — for general debug logging. Only prints in debug mode.

    debug_print_default

    fn debug_print_default() -> Unit

    Debug print for default engine creation (with middleware).

    debug_print_listen

    fn debug_print_listen(addr : String) -> Unit

    Debug print for server start.

    debug_print_new

    fn debug_print_new() -> Unit

    Debug print for engine creation.

    debug_print_route

    fn debug_print_route(meth : String, path : String) -> Unit

    Debug print for route registration.

    debug_print_warning

    fn debug_print_warning(msg : String) -> Unit

    Debug warning print — logged at Warn level.

    default

    fn default() -> Engine

    Create a new Engine pre-configured with logger and recovery middleware. This is the recommended starting point for most applications.

    dir_listing

    async fn dir_listing(dir_path : String) -> String

    Dir returns an HTML directory listing for the given path. Returns an HTML directory listing.

    ctx.html(200, dir_listing("./public"))

    disable_bind_validation

    fn disable_bind_validation() -> Unit

    Disable automatic validation during binding.

    enable_bind_validation

    fn enable_bind_validation() -> Unit

    Enable automatic validation during binding.

    gin_bind

    async fn[T :
    FromJson
    ] gin_bind(ctx : Context, _obj : T) -> T?

    Bind is a standalone function that auto-detects content type and binds. Auto-detect content type and bind - alias.

    guess_content_type

    fn guess_content_type(path : String) -> String

    Guess MIME type from file extension.

    gzip

    fn gzip(config : GzipConfig) -> (async (Context) -> Unit)

    Gzip compression middleware.

    app.use(gzip(GzipConfig::default()))
    fn h(entries : Array[(String, Json)]) -> Map[String, Json]

    h is a shortcut for building a Map[String, Json] — convenient for JSON responses.

    ctx.json(200, Json::object(h([ ("message", Json::string("hello")), ("code", Json::number(200.0)), ])))

    is_bind_validation_enabled

    fn is_bind_validation_enabled() -> Bool

    Check if bind validation is enabled.

    is_debug

    fn is_debug() -> Bool

    Whether the framework is in debug mode.

    is_excluded_type

    fn is_excluded_type(content_type : String, excluded : Array[String]) -> Bool

    Check if the content type starts with any of the excluded prefixes.

    is_included_type

    fn is_included_type(content_type : String, included : Array[String]) -> Bool

    Check if the content type is in the list of compressible types.

    is_release

    fn is_release() -> Bool

    Whether the framework is in release mode.

    is_websocket_request

    fn is_websocket_request(ctx : Context) -> Bool

    Check if a request is a WebSocket upgrade request (convenience wrapper).

    join_strings

    fn join_strings(strings : Array[String], sep : String) -> String

    Join an array of strings with a separator.

    load_html_files

    async fn load_html_files(files : Array[String]) -> Unit

    Load HTML templates from file paths.

    load_html_glob

    async fn load_html_glob(pattern : String) -> Unit

    Load HTML templates from a glob pattern.

    logger

    fn logger() -> (async (Context) -> Unit)

    Logger middleware — logs each request with method, path, status, and latency.

    Logs via the structured logging system (moonbit-log), respecting configured log levels and formats.

    For more advanced logging (JSON, fields, levels), see structured_logger().

    Usage:
    app.use(logger())

    metrics_middleware

    fn metrics_middleware(collector : MetricsCollector) -> (async (Context) -> Unit)

    Metrics middleware — records request metrics into the provided collector.

    let metrics = MetricsCollector::new() app.use(metrics_middleware(metrics)) app.get("/metrics", [fn(ctx) { ctx.json(200, metrics.to_json()) }])

    mode

    fn mode() -> Mode

    Get the current running mode.

    name_and_version

    fn name_and_version() -> String

    Framework name and version for User-Agent headers.

    new

    fn new() -> Engine

    Create a new Engine with no default middleware. For a production-ready engine with logger and recovery, use mbit.default().

    parse_content_range

    fn parse_content_range(header : String) -> ContentRange?

    Parse a Content-Range header value.

    Supports format: bytes <start>-<end>/<total>

    let range = parse_content_range("bytes 0-499/1024") assert_eq(range.start, 0) assert_eq(range.end, 499) assert_eq(range.total, 1024)

    rate_limit

    fn rate_limit(config : RateLimitConfig) -> (async (Context) -> Unit)

    Enhanced rate limiter middleware supporting multiple strategies.

    recovery

    fn recovery() -> (async (Context) -> Unit)

    Recovery middleware — catches panics/exceptions in handlers and returns 500.

    See recovery.go equivalent.

    Usage:
    app.use(recovery())

    request_id

    fn request_id() -> (async (Context) -> Unit)

    Request ID middleware — generates a unique ID per request and adds it to the response header.
    fn search(node : Node, segments : Array[String], idx : Int, params : Map[String, String]) -> MatchResult?

    Recursively search the trie for a matching route.

    secure

    fn secure(config : SecureConfig) -> (async (Context) -> Unit)

    Secure headers middleware with optional CSRF protection.

    // Web app app.use(secure(SecureConfig::default())) // With CSRF protection app.use(secure(SecureConfig::default().with_csrf())) // API server app.use(secure(SecureConfig::api_default()))

    set_html_template

    fn set_html_template(name : String, content : String) -> Unit

    Set a single HTML template by name and content.

    set_mode

    fn set_mode(mode : Mode) -> Unit

    Set the framework running mode.

    set_mode(Release)

    set_template_delims

    fn set_template_delims(left : String, right : String) -> Unit

    Set the HTML template delimiters.

    split_path

    fn split_path(path : String) -> Array[String]

    Split a path into non-empty segments.

    static_files

    fn static_files(prefix : String, dir : String) -> (async (Context) -> Unit)

    Static file serving — serves files from a local directory under a URL prefix.

    See fs.go equivalent.

    Usage:
    app.use(static_files("/static", "./public"))

    status_text

    fn status_text(code : Int) -> String

    Standard HTTP status codes used by the framework

    strip_query

    fn strip_query(path : String) -> String

    Strip query string from path.

    structured_logger

    fn structured_logger(config : StructuredLogConfig) -> (async (Context) -> Unit)

    Structured request logging middleware — powered by moonbit-log.

    Logs each request with structured fields:
    • method, path, status, latency
    • client_ip, user_agent, content_type
    • request_id (if set in context)

    app.use(structured_logger(StructuredLogConfig::default()))

    timing

    fn timing() -> (async (Context) -> Unit)

    Timing middleware — adds an X-Response-Time header with the request latency.

    upgrade_websocket

    async fn upgrade_websocket(ctx : Context) -> WebSocket?

    Attempt to upgrade an HTTP connection to WebSocket. Returns Some(WebSocket) on success, None if the request is not a valid WebSocket upgrade request.

    Performs the WebSocket opening handshake (RFC 6455 Section 4):
    1. Validates the Upgrade, Connection, and Sec-WebSocket-Key headers
    2. Computes the accept key
    3. Sends the 101 Switching Protocols response

    url_decode

    fn url_decode(s : String) -> String

    Simple URL percent-decoding.

    url_encode

    fn url_encode(s : String) -> String

    Percent-encode a URL path component.

    url_path_decode

    fn url_path_decode(s : String) -> String

    Percent-decode a URL path component.

    validate

    fn[T] validate(_value : T) -> String?

    Validate a value and return the first error message, or None if valid. Override this in your types for custom validation.

    validate_field

    fn validate_field(value : String, rules : Array[Rule]) -> String?

    Validate a single field value against a list of rules. Returns Some(error_message) if validation fails, None if valid.

    validate_map

    fn validate_map(values : Map[String, String], field_rules : Array[FieldRules]) -> ValidationErrors

    Validate a map of field values against field rules. Returns ValidationErrors containing all validation failures.

    let values = Map([("username", "alice"), ("email", "alice@example.com")]) let rules = [ FieldRules::new("username", [Required, Min(3)]), FieldRules::new("email", [Required, Email]), ] let errors = validate_map(values, rules) if errors.has_errors() { ctx.json(400, errors.to_json()) return }

    validate_or_abort

    async fn validate_or_abort(ctx : Context, values : Map[String, String], rules : Array[FieldRules]) -> Bool

    Convenience: validate and abort with 422 if there are errors. Returns true if valid, false if aborted.

    let values = Map([("username", "alice")]) let rules = [FieldRules::new("username", [Required, Min(3)])] if !validate_or_abort(ctx, values, rules) { return } // Proceed with valid data...

    version

    fn version() -> String

    Framework version string.

    websocket_handler

    fn websocket_handler(handler : async (WebSocket) -> Unit) -> (async (Context) -> Unit)

    Create a WebSocket route handler that automatically performs the upgrade and calls the provided message handler.

    app.get("/ws", [websocket_handler(fn(ws) { ws.send_text("Hello via WebSocket!") loop { match ws.read() { Some(WebSocketMessage::Text(t)) => ws.send_text("Echo: " + t) Some(WebSocketMessage::Close(_, _)) => break _ => () } } })])