Jetzig Framework Documentation

repository·main·Indexed 23 days ago

https://github.com/jetzig-framework/jetzig

A high-performance web framework written in 100% pure Zig for Linux, OS X, and Windows. Jetzig provides a modern developer experience featuring file-system routing, automatic response type inference, Zmpl templating, and built-in support for sessions, cookies, and background jobs. It includes a comprehensive CLI for project initialization, database management (migrations, seeding, and reflection), user authentication, and deployment bundling. Compatible with the latest Zig nightly master build.

Tokens
9.2K
Snippets
27
Records
71
Agent score
81%

What's inside Jetzig

  1. Overview of Jetzig Framework

    main

    Jetzig is a web framework written in 100% pure Zig designed for Linux, OS X, Windows, and any OS capable of compiling Zig code. It provides tools for building modern web applications, including file system-based routing, HTML/JSON responses, templating via Zmpl, and middleware support.

    Important Compatibility Note: The main branch of Jetzig is designed to be compatible with the latest Zig nightly master build. Older versions of Zig are not supported.

  2. Core Features of Jetzig

    main

    Jetzig includes the following built-in capabilities:

    • Routing & Content: File system-based routing with [slug] matching, custom/non-conventional routes, and static content generation from the /public directory.
    • Responses: HTML and JSON responses (inferred from extension or Accept header), MIME type inference, and a JSON-compatible response data builder.
    • Web Standards: Sessions, Cookies, Request/response headers, and Param/JSON payload parsing.
    • Development & Lifecycle: Development server auto-reload, per-request arena allocator, and stack trace output on error.
    • Advanced Features: Middleware interface, Email delivery, Background jobs, General-purpose cache, and Database integration.
    • Testing: Testing helpers for HTTP requests and responses.
  3. Use Dynamic Markdown Routes

    main

    Jetzig automatically renders Markdown files stored within the src/app/views/ directory if their file path matches a requested URI.

    For example, a file located at src/app/views/nested/route/markdown.md can be accessed via the following URIs:

    • /nested/route/markdown.html
    • /nested/route/markdown

    This feature is highly effective for scenarios requiring dynamic content loading, such as integrating with htmx.

  4. Configure Mailer Zmpl templates

    main

    A mailer can provide two Zmpl templates to render email content. These templates must be located in the mailer's specific directory:

    • HTML Template: src/app/mailers/<mailer-name>/html.zmpl
    • Text Template: src/app/mailers/<mailer-name>/text.zmpl

    The params argument passed to the deliver function contains the data used to populate these templates.

  5. How request processing works in Jetzig

    main

    When a request is received, the server executes processNextRequest, which follows this lifecycle:

    1. Initialization: Binds a database connection to the request's arena and initializes the Jetzig Request and Response objects.
    2. Routing & Processing: Executes the request logic via request.process().
    3. Middleware (Post-Process): Runs afterRequest middleware.
    4. Rendering:
      • If a middleware has already rendered a response or redirected, it handles that immediately.
      • Otherwise, it attempts to match static resources, middleware routes, custom routes, or standard routes.
      • It renders the view based on the requested format (HTML, JSON, or UNKNOWN).
    5. Response Lifecycle:
      • Appends the Content-Type header.
      • Runs beforeResponse middleware.
      • Sends the response via request.respond().
      • Runs afterResponse middleware.
    6. Cleanup: Runs deinit on middleware data and logs the request.
  6. Define a global value for `request.server.global`

    main

    To make a custom global object available throughout your application via the request object, define a constant named Global in your src/main.zig.

    When calling App.start, pass this value into the options.global field of AppOptions. Note that AppOptions.global expects an *anyopaque pointer.

  7. Implement a middleware module

    main

    A Jetzig middleware is a struct that implements several lifecycle hooks. You can define custom data fields on the struct to persist state across the request lifecycle.

    Lifecycle Hooks:

    • init(request: *jetzig.http.Request) !*Self: Initializes the middleware. Use this to allocate the middleware instance and set initial custom data.
    • afterRequest(self: *Self, request: *jetzig.http.Request) !void: Invoked after the request is received but before processing starts. Calling request.render or request.redirect here stops the middleware chain.
    • beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void: Invoked immediately before the response renders. Use this to modify the response.
    • afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) void: Invoked after the response is finalized and sent. Useful for logging, but modifications to the response here have no effect.
    • deinit(self: *Self, request: *jetzig.http.Request) void: Used for manual cleanup. Note that request.allocator is an arena allocator, so most allocations are freed automatically before the next request.
    const std = @import("std");
    const jetzig = @import("jetzig");
    
    const Self = @This();
    
    my_custom_value: []const u8,
    
    pub fn init(request: *jetzig.http.Request) !*Self {
        var middleware = try request.allocator.create(Self);
        middleware.my_custom_value = "initial value";
        return middleware;
    }
    
    pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
        self.my_custom_value = @tagName(request.method);
    }
    
    pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
        // Modify response here
    }
    
    pub fn afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) void {
        request.allocator.destroy(self);
    }
    
    pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
        request.allocator.destroy(self);
    }
  8. How `App.route()` determines view types

    main

    When using App.route(), Jetzig automatically determines the signature requirements of the view function based on the provided path string:

    • without_id: Used when the path contains no segments starting with :.
    • with_id: Used when the path contains a segment starting with : (e.g., /user/:id).
    • with_args: Used when a segment starts with : and ends with * (e.g., /files/*path), indicating a wildcard/catch-all parameter.

    Jetzig also supports a legacy mode for view functions that end with a *jetzig.Data parameter.

  9. How static views work in Jetzig

    main

    When you generate an action with the :static suffix, Jetzig treats it as a StaticRequest.

    1. Build Time: Jetzig uses a static_params constant defined in your view file to pre-render the content for specific parameter sets.
    2. Run Time: Requests matching the pre-rendered parameters will serve the cached content instead of executing the logic dynamically.

    If you use :static, the generator will also create a static_params block in your .zig file to define which parameter sets should be pre-rendered.

  10. Initialize authentication with `jetzig auth init`

    main

    Run jetzig auth init to generate a migration file that creates a users table. The migration includes the following schema:

    • email: string, indexed, and unique.
    • password_hash: string.

    Note: After running this command, you must run jetzig database update to apply the migration to your database.

    jetzig auth init
  11. Initialize a new Jetzig project with `jetzig init`

    main

    The jetzig init command scaffolds a new Jetzig project. It creates the necessary project structure, including build.zig, build.zig.zon, src/main.zig, and an example view with a template.

    If you run the command without arguments, it will interactively prompt you for a project name and an installation path. You can also specify a path directly using the --path flag or as a positional argument.

    Once initialized, you can launch the development server using zig build run or jetzig server.

  12. Register a generated middleware in `src/main.zig`

    main

    After generating a middleware, you must manually register it in your application's configuration. Add the new module to the jetzig_options.middleware array in src/main.zig.

    Middleware are invoked in the order they appear in the jetzig_options.middleware declaration.

    pub const jetzig_options = struct {
        pub const middleware: []const type = &.{
            @import("app/middleware/IguanaBrain.zig"),
        };
    };