Add middleware or a hook#
This example adds an X-Example response header globally and a private-cache hook to one route. Middleware also wraps missing routes; hooks are scoped to matched routes. Use guards for authorization decisions based on read-only request state.
1. Create the middleware module#
Create src/middleware/ResponseHeader.zig (and its parent directory):
// src/middleware/ResponseHeader.zig
const zyn = @import("zyn");
pub const Config = struct { value: []const u8 };
pub fn handle(comptime config: Config, middleware: *zyn.Middleware) !void {
try middleware.headers.set("X-Example", config.value);
try middleware.next();
}
2. Register the middleware#
Add the module import to src/app.zig:
// src/app.zig: imports
const ResponseHeader = @import("middleware/ResponseHeader.zig");
Append this entry to the existing pub const middleware: zyn.MiddlewareStack array. Retain the existing middleware and its configuration, including request logger filtering and the order of method override:
// src/app.zig: middleware entries
.use(ResponseHeader, .{ .value = "recipe" }),
Defining handle alone does not install middleware.
3. Add and attach a route hook#
// src/actions/cache_example.zig
const zyn = @import("zyn");
pub fn private_cache(hook: *zyn.Hook) !void {
try hook.headers.set("Cache-Control", "private");
try hook.next();
}
pub fn show(action: *zyn.Action) !zyn.Response {
return action.json(.{ .message = "Example" }, .{});
}
Add the import and route entry to the existing src/routes.zig declarations:
// src/routes.zig: imports
const cache_example = @import("actions/cache_example.zig");
// src/routes.zig: table entries
.get("/cache-example", cache_example.show, &.{.hook(cache_example.private_cache)}),
For a hook shared by every route in a module, add .hook(cache_example.private_cache) to its existing defaults instead. Do not replace existing CSRF settings, layouts, guards, or hooks.
Call next() once when continuing. To short-circuit, use return middleware.respond(...) or return hook.respond(...); respond itself does not exit the callback. Code after try next() runs only on success. Use defer for non-fallible cleanup on success and failure, or catch to handle errors. Response changes after next() require an uncommitted response. Keep request facts in typed Locals and request allocations in the supplied arena.
4. Verify global and route scope#
zyn routes
zyn check
zyn dev --http
In another terminal:
curl -i http://localhost:7532/cache-example
curl -i http://localhost:7532/no-such-route
The first response is 200, has X-Example: recipe and `Cache-Control: private, and contains {"message":"Example"}`. The missing-route response is 404 and still has X-Example: recipe, but does not receive the route hook's private-cache header. Use the server's reported port if it differs.