README

RabitLogic/mbit/core does not have a README file

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

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

#
Context

pub(all) struct Context {
req :
Request

reader : &
Reader

conn : ResponseConn
params : Map[String, String]
query_cache : Map[String, String]?
body : String?
body_bytes : Bytes?
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_bytes

async fn Context::body_bytes(self : Context) -> Bytes

Read the full request body as raw bytes (lazy, cached). Unlike body_string(), this preserves arbitrary binary content and never fails on non-UTF-8 payloads (e.g. binary file uploads).

#
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). For binary bodies use body_bytes(); text decoding of non-UTF-8 payloads yields an empty string (same as before).

#
Context::client_ip

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

The client's IP address.

#
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) -> Bytes?

FormFile returns the content of an uploaded file from a multipart form. The file is identified by the form field name.

Returns Some(content) if the file was found, None otherwise.

#
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 filename from a multipart form file 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.

Note: the underlying HTTP server lower-cases all request header keys (moonbitlang/async/http parser does to_lower()), so lookups fall back to the lower-cased key to stay correct under real HTTP traffic.

#
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 using the global template store.

#
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, String]

MultipartForm returns the full multipart form data as parsed fields. Returns a map of field name → file content for simple file uploads.

#
Context::must_bind

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

Like should_bind but raises a catchable Failure (recovered to 400/500 by recovery()) if binding fails. Deliberately uses raise(Failure(...)) instead of abort() so a bad request cannot crash the server.

#
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, raising a catchable Failure (recovered by recovery()) instead of aborting on failure.

#
Context::must_get

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

MustGet returns the value for the given key if it exists, otherwise raises a catchable Failure (recovered to 500 by recovery()). Do NOT use abort() here — it is a hard panic that terminates the whole process.

#
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_file

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

SaveUploadedFile saves an uploaded file to the specified destination path. Accepts raw Bytes so binary files are written verbatim.

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

#
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]
}

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. Useful for load balancer health checks, container probes, and monitoring.

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 addr with graceful shutdown support. This is a blocking call — it runs until shutdown() is invoked (or the process is killed). On shutdown it stops accepting new connections, waits for in-flight requests to complete, then closes idle keep-alive connections before returning.

#
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 @tls.Tls::server_from_pair to wrap raw TCP in TLS, then delegates to the standard HTTP request handler.

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

#
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. Stops accepting new connections, waits for in-flight requests to complete, then closes idle keep-alive connections. run() returns afterwards. 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, mw : 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.

#
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, mw : 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.

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

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

#
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

#
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, mw : async (Context) -> Unit) -> Unit

Add global middleware (applied to every route).

#
Router::use_on

fn Router::use_on(self : Router, meth : Method, pattern : String, mw : 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.

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

#
ServerControl

pub(all) struct ServerControl {
shutting_down : Bool
active_requests : Int
open_connections : Array[
ServerConnection
]
open_tasks : Array[
Task
[Unit]]
drain_cond :
Cond

server :
Server
?
}

Runtime control state shared between Engine::run() (the server loop) and Engine::shutdown(). A single process runs at most one server, so a module global is sufficient. Used to implement graceful shutdown: stop accepting, drain in-flight requests, then close idle keep-alive connections.

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

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

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

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

#
clean_path

fn clean_path(path : String) -> String

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

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

#
default

fn default() -> Engine

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

#
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.
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_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.

#
logger

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

Logger middleware — logs each request with method, path, status, and latency via the structured logging system (moonbit-log), respecting configured log levels and formats. 5xx -> error, 4xx -> warn, otherwise info.

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.

#
new

fn new() -> Engine

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

#
recovery

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

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

See recovery.go equivalent.

Usage:
app.use(recovery())
fn search(node : Node, segments : Array[String], idx : Int, params : Map[String, String]) -> MatchResult?

Recursively search the trie for a matching route.

#
set_mode

fn set_mode(mode : Mode) -> Unit

Set the framework running mode.

set_mode(Release)

#
split_path

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

Split a path into non-empty segments.

#
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()))

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

#
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 _ => () } } })])