docs > reference > dependencies reference

Dependencies Reference

Overview

Zigmund's dependency injection system allows routes to declare named dependencies that are resolved before the handler runs. Dependencies can be cached per-request or per-app, can depend on other dependencies (forming a DAG), and support automatic cleanup.

Depends Marker

Depends

pub fn Depends(comptime provider: anytype, opts: DependsOptions) type

Declares a dependency on a provider function. The provider is called during request dispatch, and its return value is injected into the handler parameter.

The provider function signature must be:

fn(req: *Request, allocator: std.mem.Allocator) !?[]const u8

Or any subset of those parameters.

DependsOptions

Field Type Default Description
use_cache bool true Cache the resolved value
cache_scope DependsCacheScope .request Cache lifetime scope
name ?[]const u8 null Named dependency identifier
depends_on []const []const u8 &.{} Names of prerequisite dependencies
cleanup ?DependencyCleanupFn null Function called after the request to clean up

DependsCacheScope

pub const DependsCacheScope = enum {
    request,  // Cached for the duration of one request
    app,      // Cached for the lifetime of the application
};

DependencySpec

Used in RouteOptions.dependencies to declare named dependencies on routes.

Field Type Default Description
name []const u8 (required) Dependency name
required bool true Whether the dependency must resolve
use_cache bool true Use cached values
cache_scope DependencyCacheScope .request Cache scope
depends_on []const []const u8 &.{} Prerequisite dependency names
scopes []const []const u8 &.{} Required security scopes

App-Level Dependency Management

Registration

// Register a named dependency
try app.addDependency("auth", authResolver);

// Register with cleanup
try app.addDependencyWithCleanup("db", dbResolver, dbCleanup);

Overrides (for testing)

// Override a dependency resolver
try app.overrideDependency("db", mockDbResolver);

// Clear a specific override
_ = app.clearDependencyOverride("db");

// Clear all overrides
app.clearDependencyOverrides();

Dependency Graph

Dependencies can declare prerequisites via depends_on. The framework resolves them in topological order (prerequisites first). Circular dependencies are detected and result in a DependencyCycleDetected error.

Cleanup Functions

The cleanup function signature is:

fn cleanup(req: *Request, name: []const u8, value: []const u8, allocator: std.mem.Allocator) !void

Cleanup runs after the response is sent, in reverse order of resolution.

Example

const std = @import("std");
const zigmund = @import("zigmund");

fn authResolver(req: *zigmund.Request, allocator: std.mem.Allocator) !?[]const u8 {
    _ = allocator;
    const token = req.header("authorization") orelse return error.Unauthorized;
    return token;
}

fn dbResolver(req: *zigmund.Request, allocator: std.mem.Allocator) !?[]const u8 {
    _ = req;
    _ = allocator;
    return "db-session-123";
}

fn dbCleanup(req: *zigmund.Request, name: []const u8, value: []const u8, allocator: std.mem.Allocator) !void {
    _ = req;
    _ = name;
    _ = value;
    _ = allocator;
    // Close database connection here
}

pub fn main() !void {
    var app = try zigmund.App.init(allocator, .{ .title = "API", .version = "1.0" });

    try app.addDependency("auth", authResolver);
    try app.addDependencyWithCleanup("db", dbResolver, dbCleanup);

    try app.get("/data", dataHandler, .{
        .dependencies = &.{
            .{ .name = "auth" },
            .{ .name = "db", .depends_on = &.{"auth"} },
        },
    });
}

fn dataHandler(req: *zigmund.Request, allocator: std.mem.Allocator) !zigmund.Response {
    const db = req.dependency("db").?;
    return zigmund.Response.json(allocator, .{ .session = db });
}