Router API¶
Route¶
A single route definition combining an HTTP method, a URL pattern, and a handler function.
Route.init(method, pattern, handler) Route¶
Create a route entry.
- method: Method — HTTP method (.get, .post, .put, .patch, .delete, .options, .head)
- pattern: []const u8 — URL pattern (e.g. "/users/:id")
- handler: *const fn (*Request, *Response) void — request handler
Route.validatePattern(pattern) !void¶
Validate a path pattern at comptime. Called automatically by Router.init. Returns an error for invalid patterns:
- error.InvalidPattern — empty pattern, not starting with /, multiple wildcards, wildcard not last, empty param names, duplicate params
Route.matchPath(pattern, actual, params) bool¶
Match a concrete path against a pattern at runtime, extracting named parameters into the provided RouteParams. Returns true on match.
RouteParams¶
Zero-allocation URL parameter storage (fixed-size array of 16 entries).
Constants¶
max_params = 16— maximum number of URL parameters
Methods¶
RouteParams.init(gpa) RouteParams— create empty paramsparams.deinit()— free resourcesparams.get(name) ?[]const u8— get param value by nameparams.getAs(T, name) !T— get param parsed as typeT(e.g.params.getAs(u64, "id")); returnserror.MissingParamif not found, or parse error if value is invalid
Router¶
Comptime route table with runtime dispatch. Routes are defined at comptime; dispatch uses a linear scan with specificity scoring at runtime.
Methods¶
Router.route(method, pattern, handler) Route— create a route entry (comptime-friendly helper)Router.group(prefix, routes) []const Route— create a grouped set of routes with a common prefixRouter.init(routes, notFoundHandler) Router— initialize router from a comptime tuple/array of routes; validates all patterns at compile timeRouter.initFromSlice(routes, notFoundHandler) Router— initialize router from a runtime slice of routes (no comptime validation)router.dispatch(req, res)— dispatch a request to the matching route handler
Dispatch Algorithm¶
- For each registered route, attempt pattern match via
Route.matchPath - If path matches and method matches, score by specificity (static > named param > wildcard)
- Pick the highest-scoring match and call its handler, populating
req.params - If path matches but method doesn't, return 405 with an
Allowheader listing all valid methods for that path - If no path matches, call the
not_found_handler
Specificity Scoring¶
- Static segments (e.g.
users): 100 points - Named parameters (e.g.
:id): 10 points - Wildcards (e.g.
*): 1 point - Total score is sum across all segments
Pattern Syntax¶
| Pattern | Description |
|---|---|
/users |
Static segment — matches literally |
/users/:id |
Named parameter — matches any single path segment, value stored in params.get("id") |
/users/:id[u64] |
Typed parameter — matches and validates as the specified integer type |
/static/* |
Wildcard — matches remaining path segments, captured under "*" param name |
Route Groups¶
const routes = .{
zypher.router.Router.group("/api", .{
zypher.router.Router.route(.get, "/users", listHandler),
zypher.router.Router.route(.post, "/users", createHandler),
}),
};
// Creates: GET /api/users → listHandler
// POST /api/users → createHandler
Full Example¶
const zypher = @import("zypher");
fn listHandler(req: *zypher.core.Request, res: *zypher.core.Response) void {
res.json(.{ .users = &.{} }) catch {};
}
fn getUserHandler(req: *zypher.core.Request, res: *zypher.core.Response) void {
const id = req.params.get("id") orelse "";
res.json(.{ .id = id }) catch {};
}
fn notFound(req: *zypher.core.Request, res: *zypher.core.Response) void {
_ = res.status(404);
res.text("Not Found") catch {};
}
const routes = .{
zypher.router.Router.route(.get, "/api/users", listHandler),
zypher.router.Router.route(.get, "/api/users/:id", getUserHandler),
};
var router = zypher.router.Router.init(routes, notFound);
app.routerHandler(router.dispatch);