mbit

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

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

    #mbit

    mbit is a MoonBit web framework, transplanted from Gin. It brings Gin's trie-based router, middleware chain, and context-centric API to MoonBit, reimagined for MoonBit's type system and async model.

    A MoonBit web framework.

    MoonBit Tests Gin

    #Project Structure

    mbit is split into focused sub-packages. Everything is re-exported for your convenience through the root mbit package (Mbit wrapper), while the framework internals live in well-separated packages:

    mbit/ ├── moon.pkg.json # root package — Mbit convenience wrapper, version ├── app.mbt # `Mbit` — chainable struct API over `core.Engine` ├── version.mbt ├── core/ # the engine core (mirrors Gin's single package) │ ├── context.mbt # `Context`, `Handler`, `ResponseConn`, `TestConn` │ ├── mbit.mbt # `Engine` (`new`/`default`), `h`, `url_encode` │ ├── router.mbt # trie `Router`, `Node`, `RouteInfo` │ ├── routergroup.mbt # `Group` │ ├── binding.mbt # `bind_json` / `bind_query` / ... (tied to Context) │ ├── validation.mbt # `Rule`, `FieldRules`, `validate_map` │ ├── errors.mbt # `Method`, `status_text` │ ├── response_writer.mbt, mode.mbt, utils.mbt, path.mbt, debug.mbt, │ ├── logger.mbt # core middleware used by `default()` │ └── recovery.mbt ├── middleware/ # optional middleware (depends on `core`) │ ├── cors.mbt, gzip.mbt, auth.mbt, ratelimit.mbt, secure.mbt, requestid.mbt ├── template/ # standalone HTML template store (imported by `core`) │ └── template.mbt ├── fs/ # standalone static file serving utilities │ └── fs.mbt ├── demo/ # full-feature runnable demo └── example/ # (incomplete, not a package yet)

    Note on the core package: binding/validation/logger/recovery live in core because MoonBit forbids defining methods on foreign types — the Context::should_bind_* / must_bind_* family must live alongside Context itself, so they ship with the engine (exactly as Gin keeps Default, Logger and Recovery in one package).

    #Features

    • Trie-based router — :param and *wildcard matching, route groups, custom 404/405
    • Middleware — logger, recovery, CORS, gzip, auth, rate-limit (fixed window / sliding window / token bucket, per-route & per-key), secure headers, request ID, timing
    • Rendering — JSON, 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
    • Static files — serve single files or entire directories
    • Structured logging — Logger with levels/formats/fields (powered by moonbit-log), structured_logger() middleware, metrics_middleware() collector
    • Configuration — multi-environment config, env-var binding, validation, hot-reload hooks (Config, ServerConfig, AppEnvironment)
    • WebSocket — upgrade detection, frame encode/decode, and message handling (websocket_handler(fn(ws) { ... }))
    • Health endpoint — built-in /health probe (app.health()) for load balancers / containers

    #Relationship to Gin

    mbit transplants Gin v1.x's architecture and API design into MoonBit. The core concepts map directly, adapted to MoonBit's type system and async model.

    Gin (Go)mbit (MoonBit)
    gin.Default()default()
    gin.New()new()
    gin.ContextContext
    gin.HandlerFuncHandler = async (Context) -> Unit
    gin.Hh([]) helper
    gin.RouterGroupGroup
    engine.GET("/", handler)app.get("/", [handler])
    engine.POST("/", handler)app.post("/", [handler])
    engine.Any("/", handler)app.any("/", [handler])
    engine.Handle("GET,POST", "/", h)app.handle([GET, POST], "/", [h])
    engine.Group("/api")app.group("/api")
    engine.Use(mw)app.use(mw)
    engine.NoRoute(h)app.no_route([h])
    engine.NoMethod(h)app.no_method([h])
    engine.StaticFile("/f", "./f")app.static_file("/f", "./f")
    engine.Static("/s", "./d")app.static_("/s", "./d")
    c.Param("id")ctx.param("id")
    c.Query("q")ctx.query("q")
    c.PostForm("f")ctx.post_form("f")
    c.ShouldBindJSON(&obj)bind_json(ctx)
    c.ShouldBindQuery(&obj)bind_query(ctx)
    c.MustBindWith(&obj, binding.JSON)ctx.must_bind_json()
    c.JSON(200, gin.H{"k":"v"})ctx.json(200, Json::object(h([("k",Json::string("v"))])))
    c.XML(200, obj)ctx.xml(200, data)
    c.YAML(200, obj)ctx.yaml(200, data)
    c.HTML(200, tpl, data)ctx.html(200, html)
    c.String(200, "msg")ctx.string(200, "msg")
    c.Redirect(301, "/")ctx.redirect(301, "/")
    c.SSEvent("ev", "data")ctx.sse("ev", "data")
    c.Stream(...)ctx.stream(200, "text/plain", writer)
    c.Data(200, ct, data)ctx.data(200, ct, data)
    c.File("path")ctx.file("path")
    c.Set("key", val)ctx.set("key", Json::string(val))
    c.Get("key")ctx.get("key")
    c.AbortWithStatus(403)ctx.abort_with_status(403, "msg")
    c.Next()ctx.next()
    c.ClientIP()ctx.client_ip()
    engine.Run(":8080")app.run("0.0.0.0:8080")
    gin.Recovery()recovery()
    gin.Logger()logger()

    #Key differences

    • Async handlers: async (Context) -> Unit instead of synchronous func(*gin.Context)
    • Generics over reflection: Binding uses FromJson trait, not reflect
    • Explicit validation: FieldRules replace struct tags like binding:"required"
    • Immutable by default: mut keyword required; Map([]) for empty maps
    • Engine is chaining: Route registration returns Engine, chainable with |> ignore

    #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 (via structured Logger)
    recovery()Catches panics, returns 500
    cors(config)CORS headers
    gzip(config)Response compression
    request_id()X-Request-Id header
    timing()X-Response-Time header
    secure(config)Security headers (helmet-like), CSRF option
    auth()Basic auth
    rate_limit(config)Rate limiting — fixed window / sliding window / token bucket, per-route & per-key
    body_size_limit(bytes)Reject large payloads
    structured_logger(config)JSON structured request log (fields, levels)
    metrics_middleware(collector)In-memory request metrics collector

    #Rate limiting strategies

    // Fixed window (simple, default)
    app.use(rate_limit(RateLimitConfig::default()))

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

    // Per-route or per-user keying
    app.use(rate_limit(
    RateLimitConfig::default().with_key(fn(ctx) { ctx.client_ip() }),
    ))

    #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: 127.0.0.1 [GET] /api/users -> 200 (12ms)

    #CORS

    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"],
    }))

    #Gzip

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

    #Rate Limiting

    app.use(rate_limit(RateLimitConfig::{
    max_requests: 50,
    window_secs: 60,
    }))

    #Secure Headers

    app.use(secure(SecureConfig::default()))
    // Sets: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection,
    // Content-Security-Policy, Referrer-Policy

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

    #Configuration

    Multi-environment config with env-var binding (APP_ prefix by default):

    let cfg = Config::from_env()
    let port = cfg.get_int("PORT", default=8080)
    let db_url = cfg.get_string("DATABASE_URL", default="")
    cfg.environment() // AppEnvironment::Development / Staging / Production

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

    #Structured Logging

    Powered by moonbit-log, with levels, formats and fields:

    Logger::set_level(Info)
    Logger::set_format(JSONFormat)
    Logger::set_app_name("my-service")
    Logger::info("User logged in", fields=[("user_id", "42")])

    // Per-request JSON logging middleware
    app.use(structured_logger(StructuredLogConfig::default()))

    // In-memory metrics collector
    let m = MetricsCollector::new()
    app.use(metrics_middleware(m))

    #WebSocket

    Upgrade detection, frame encode/decode and message handling:

    app.get("/ws", [websocket_handler(fn(ws) {
    ws.send_text("Connected!")
    match ws.read_message() {
    Some(msg) => ws.send_text("Echo: " + msg.to_string())
    None => ()
    }
    })])

    #Health Endpoint

    app.health() // registers GET /health -> 200 {"status":"ok","service":"mbit"}

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

    // Graceful shutdown
    app.run_with_shutdown(":8080", on_shutdown=fn() {
    println("Cleaning up...")
    })

    // Trigger shutdown from signal handler
    app.shutdown()

    #Debug

    In debug mode (set_mode(Debug) / mbit.default()), the framework prints startup and route-registration diagnostics with a full UTC timestamp YYYY-MM-DD HH:MM:SS.mmm UTC — no [mbit] prefix:

    2026-08-02 12:34:56.789 UTC engine created with logger + recovery middleware 2026-08-02 12:34:56.790 UTC route GET / 2026-08-02 12:34:56.790 UTC route GET /hello/:name 2026-08-02 12:34:56.791 UTC listening on http://0.0.0.0:8080

    Programmatic route introspection is also available:

    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")
    }

    #API Reference

    #Mbit

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

    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

    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 (load balancer / probe checks).

    Mbit::max_multipart_memory

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

    Mbit::new

    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

    ——————————————————————————————————————————————————————————————————————

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

    name_and_version

    fn name_and_version() -> String

    Framework name and version for User-Agent headers.

    version

    fn version() -> String

    Framework version string.

    Source Files