Zig 0.17.0

Group and routes

On this page:

  • spider.Group: The routes of one feature under one prefix: what a feature's routes.build() returns.

spider.Group

zig
pub const Group = struct

A set of routes under one prefix, built by a feature and mounted on the server (server.mount(g) or server.mountFeatures(features)).

zig
pub fn build() spider.Group {
    var g = spider.Group.init("/tickets");
    _ = g
        .defaults(.{ .org_roles = ticket_roles }) // before the routes
        .get("", controller.index, .{}) // inherits the defaults
        // replaces them:
        .post("/:id/approve", controller.approve, .{ .org_roles = admin_roles })
        // opts out:
        .get("/public-feed", controller.feed, .{ .public = true });
    return g;
}

Routes take the same config as server routes (routing/route_config.zig) and plain or extractor handlers (spider.Path / spider.Form / spider.Loaded). A handler that takes the server's decorations is a compile error here.

Fields

  • router: *Router
  • prefix: []const u8
  • path_middlewares: [32]PathMiddlewareEntry = undefined
  • path_middleware_count: usize = 0
  • has_sse: bool = false
  • default_middlewares: []const MiddlewareFn = &.{}: From defaults(): RBAC middlewares and flags for routes that don't declare their own access rules.
  • default_meta: RouteMeta = .{}
  • use_middlewares: [16]MiddlewareFn = undefined: From use(): run on every route of the group, after its RBAC checks.
  • use_count: usize = 0
  • route_count: usize = 0

Group.init

zig
pub fn init(prefix: []const u8) Group

A new, empty group. prefix goes in front of every path added to it; "" is a group at the root of the site. prefix is not copied. The group's memory is never freed: build groups once, at startup.

Group.defaults

zig
pub fn defaults(self: *Group, comptime config: anytype) *Group

Access rules and flags every route of the group inherits unless the route declares its own .roles / .org_roles / .public / .authenticated / .policy: any one of them replaces all the access rules of the defaults (.quiet_log / .allow_http are overridden one by one). Must come before the group's routes: after one, it panics. A second call replaces the first. Plain sse() routes do not get the defaults.

Group.use

zig
pub fn use(self: *Group, m: MiddlewareFn) *Group

Middleware for every route of this group (applied when the group is mounted, so the order relative to the routes doesn't matter). Runs after the route's RBAC checks. Unlike useAt(), it's tied to the routes, not to a path prefix. At most 16: one more panics.

Group.get

zig
pub fn get(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a GET route under the group's prefix: in a group at /posts, "" answers /posts and "/:id" answers /posts/4. Path and config are the ones of Server.get; an empty config (.{}) takes the group's defaults. The handler is fn (*spider.Ctx) !spider.Response, with spider.Path, spider.Form and spider.Loaded parameters if it wants them.

Group.post

zig
pub fn post(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a POST route under the group's prefix. Path, handler and config as in get.

Group.put

zig
pub fn put(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a PUT route under the group's prefix. Path, handler and config as in get.

Group.delete

zig
pub fn delete(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a DELETE route under the group's prefix. Path, handler and config as in get.

Group.patch

zig
pub fn patch(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a PATCH route under the group's prefix. Path, handler and config as in get.

Group.head

zig
pub fn head(self: *Group, path: []const u8, handler: anytype, comptime config: anytype) *Group

Adds a HEAD route under the group's prefix. Path, handler and config as in get.

Group.sse

zig
pub fn sse(self: *Group, path: []const u8, comptime handler: fn (*Sse) anyerror!void) *Group

Adds a Server-Sent Events route under the group's prefix. It takes no config and does not get the group's defaults: use sseWith to say who may call it.

Group.sseWith

zig
pub fn sseWith(self: *Group, path: []const u8, comptime handler: fn (*Sse) anyerror!void, comptime config: anytype) *Group

sse() with a route config, like get(): the group's defaults() apply unless the config declares its own access. (sse() takes neither.)

Group.useAt

zig
pub fn useAt(self: *Group, path_suffix: []const u8, m: MiddlewareFn) *Group

Adds a middleware for the group's prefix followed by path_suffix, and everything under that path (whole segments, like Server.useAt). It is tied to the path, not to the routes: use is the one for "every route of this group". At most 32 per group, and 32 in the server the group is mounted on: one more panics.

What the declarations above take or give back, without a name of its own in spider.

RouteMeta

zig
pub const RouteMeta = struct

What a route declares about itself, from its config (.{ .roles, .org_roles, .public, .authenticated, .policy, .quiet_log, .allow_http }, see routing/route_config.zig). Available to middlewares and handlers as c.route(); every field is at its default for a request no route matched.

Fields

  • public: bool = false: No login needed: auth middlewares (jwks/keycloak/clerk, auth, spider.session) let the request through.
  • quiet_log: bool = false: A successful request isn't logged (heartbeats, polling); 4xx/5xx still are.
  • authenticated: bool = false: Any logged-in user (.authenticated = true): 401 without an identity.
  • allow_http: bool = false: Served over plain HTTP even when the app forces HTTPS (spider.forceHttps) — for clients that can't do TLS.
  • roles: []const []const u8 = &.{}: Informational copy of the RBAC config (the checks themselves are the route's middlewares): shown by the route listing.
  • org_roles: []const []const u8 = &.{}: The route's .org_roles, informational like roles.
  • policy: ?[]const u8 = null: The name of the route's .policy (spider.policy, spider.resourcePolicy, a policySet's route), if any.

RouteMeta.declaresAccess

zig
pub fn declaresAccess(m: RouteMeta) bool

The route says who may call it: .public, .authenticated, .roles / .org_roles, or .policy.

RouteMeta.writeAccess

zig
pub fn writeAccess(m: RouteMeta, w: *std.Io.Writer) !void

Writes the access column of the route listing: "public", "roles:a,b", "org:a,b", "org:a roles:b", "authenticated", or "-" (nothing declared), followed by " policy:name" when the route has one ("policy:name" alone for a route with only a policy). A route with roles shows them and not "authenticated".