flare_mlake · API · http

flare.http.router

HTTP request router with method dispatch + path parameters.

A Router is a Handler that dispatches each request to a per-(method, path) inner handler. It supports:

  • Literal segments: "/users" matches exactly /users.
  • Parameter segments: ":id" matches one segment of any non-empty value; the captured value lands in req.params[name].
  • Wildcard tail: a final segment "*" matches the rest of the path (captured as req.param("*"), including path separators).
  • Method dispatch: get / post / put / patch / delete / head register one handler per (method, path) pair.

Unknown paths return 404 Not Found; known paths called with the wrong method return 405 Method Not Allowed with a synthesised Allow: header listing the supported methods.

Sub-router mounting (mount(prefix, sub)) attaches a whole nested Router under a literal path prefix (e.g. /api/v1). The mounted router is owned by value behind the same ArcPointer refcount the struct-handler registry uses, so Router copies (one per worker) share the mounted subtree without double-freeing it. A mounted prefix owns its entire subtree: direct routes registered on the parent take priority, and any unmatched request whose path starts with the prefix is delegated (with the prefix stripped) to the sub-router, which runs its own method dispatch and 404/405 synthesis.

Example:

from flare.http import Router, Request, Response, ok, not_found

def home(req: Request) raises -> Response:
    return ok("home")

def get_user(req: Request) raises -> Response:
    return ok("user " + req.param("id"))

def main() raises:
    var r = Router()
    r.get("/", home)
    r.get("/users/:id", get_user)

    # `r` is a Handler; pass it to HttpServer.serve.

This first release uses a simple runtime match (linear scan per depth plus a per-entry segment compare). A compile-time trie lives on the roadmap; the public Router API will not change when the trie lands, only the internal representation.

Structs

Router

struct Router

HTTP router with method dispatch, path parameters, and nesting.

Accepts both plain def(Request) raises -> Response functions (wrapped internally in FnHandler) and arbitrary H: Handler & Copyable structs (boxed via _StructHandler with monomorphised serve / destroy thunks). Use the latter to register Extracted[H](), app-state-bearing handlers, middleware wrappers, and any other stateful Handler.

Routes are stored as (method, compiled-segments, handler-kind, handler-index) quadruples; handler-kind selects _handlers (FnHandler list) or _struct_handlers (boxed Handler structs) for the actual call site.

Example:

var r = Router()
r.get("/", home) # def(Request)
r.get("/users/:id", Extracted[GetUser]()) # Handler struct

Implements Copyable, Defaultable, Handler, Movable

comptime BodyType = InlineBody

The Body impl this handler authors its response with.

__init__

def __init__(out self)

Create an empty router (no routes).

get

def get(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for GET path.

Args
path: Route pattern (e.g. "/users/:id").
handler: The function to call on a match.
def get[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for GET path.

H is type-erased into _StructHandler via heap allocation + monomorphised serve / destroy thunks; the per-request dispatch is a direct call through the cached thunk pointer with no trait-table lookup.

Use this overload to register Extracted[H](), app-state handlers, middleware-wrapped handlers, and any user-defined Handler struct. The def(Request) raises -> Response overload above continues to work unchanged.

Args
path: Route pattern (e.g. "/users/:id").
handler: A Handler & Copyable instance; ownership transfers into the Router (the Router owns the heap allocation and frees it in its destructor).

post

def post(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for POST path.

def post[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for POST path. See get[H] for the type-erasure shape.

put

def put(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for PUT path.

def put[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for PUT path. See get[H] for the type-erasure shape.

patch

def patch(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for PATCH path.

def patch[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for PATCH path. See get[H] for the type-erasure shape.

delete

def delete(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for DELETE path.

def delete[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for DELETE path. See get[H] for the type-erasure shape.

head

def head(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for HEAD path.

def head[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for HEAD path. See get[H] for the type-erasure shape.

options

def options(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for OPTIONS path.

def options[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for OPTIONS path. See get[H] for the type-erasure shape.

trace

def trace(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for TRACE path.

def trace[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for TRACE path. See get[H] for the type-erasure shape.

connect

def connect(mut self, path: String, handler: def(Request) raises thin -> Response)

Register handler for CONNECT path.

def connect[H: Handler & Copyable](mut self, path: String, var handler: H)

Register handler (a Handler struct) for CONNECT path. See get[H] for the type-erasure shape.

fallback

def fallback(mut self, handler: def(Request) raises thin -> Response)

Set a custom fallback handler.

Invoked when no registered route matched and no mounted sub-router claimed the path. Replaces the default 404 body (the 405 path for a known path under other methods still fires first). Registering twice keeps the last handler.

mount

def mount(mut self, prefix: String, var sub: Self)

Mount sub under the literal path prefix.

Every request whose path starts with prefix (and which no directly-registered route on this Router already handled) is delegated to sub with prefix stripped, so sub sees paths relative to its mount point. sub performs its own method dispatch and 404/405 synthesis.

The sub-router is boxed into the shared struct-handler registry (heap-allocated, monomorphised destroy thunk) so it is owned by value and freed exactly once on the last Router drop -- the same ownership model as struct handlers.

Args
prefix: A literal path prefix, e.g. "/api/v1". Parameter (:id) and wildcard (*) segments are rejected.
sub: The sub-router to mount; ownership transfers in.

route_count

def route_count(self) -> Int

Number of registered top-level routes (excludes mounts).

route_method

def route_method(self, i: Int) -> String

HTTP method of route i (uppercase, e.g. GET).

route_openapi_template

def route_openapi_template(self, i: Int) -> String

Route i's path as an OpenAPI template: :id -> {id}, wildcard * -> {path} (/ for the empty root).

route_path_params

def route_path_params(self, i: Int) -> List[String]

Names of route i's path parameters (:name segments).

serve

def serve(self, req: Request) -> Response

Dispatch req to the matching handler.

Returns the handler's response, or a 404 / 405 if no route matches.