///
Ztack's `router.zig` module provides a robust and type-safe mechanism for defining and managing HTTP routes in your Zig web applications. It allows you to map incoming HTTP requests (based on method a
37 views
~37 views from guests
Guest views are estimated from total page views. These include anonymous visitors and users who weren't logged in when they viewed the page.
Ztack's router.zig module provides a robust and type-safe mechanism for defining and managing HTTP routes in your Zig web applications. It allows you to map incoming HTTP requests (based on method and path) to specific handler functions, simplifying the process of building backend APIs and server-side rendered pages.
This guide will walk you through the key concepts and practical steps for setting up and using Ztack's routing capabilities.
Before diving into implementation, let's understand the fundamental components of Ztack's routing system:
HttpMethod: This enum ([Ztack API Reference]) defines the standard HTTP request methods (e.g., GET, POST, PUT, DELETE). When registering a route, you specify which HTTP method it responds to.
Router Struct: The Router struct ([Ztack API Reference]) is the central component for managing your application's routes. You initialize a Router instance, register your handler functions with it, and then use it to dispatch incoming requests to the appropriate handlers.
Request Handler Functions: These are Zig functions that define the logic to execute when a specific route is matched. They receive information about the incoming request and are expected to return an HTTP response.
std.mem.Allocator: Like many Zig data structures, the Router requires an allocator for managing its internal memory, such as storing registered routes.
The first step is to create an instance of the Router struct. This typically happens in your main function or application initialization logic.
std.heap.GeneralPurposeAllocator as a common choice for application-level allocations.app.deinit() is crucial for releasing memory allocated by the router when it's no longer needed.Request handlers are the functions that execute when a route matches an incoming request. All handler functions in Ztack's router share a specific signature:
Let's break down the handler's signature:
method: router.HttpMethod: The HTTP method of the incoming request (e.g., .GET, .POST).path: []const u8: The URL path of the request (e.g., /, /api/data).body: []const u8: The raw body of the HTTP request. For GET requests, this will typically be empty.![]const u8: The handler must return a std.errors.Error or a []const u8 representing the complete HTTP response string (including headers and body).Here's an example handler that generates a simple HTML page:
Ztack provides convenient helper functions in the router module for common response types ([Ztack API Reference]):
router.htmlResponse(allocator, content: []const u8) ![]const u8: Constructs a 200 OK HTML response with the given content.router.jsonResponse(allocator, content: []const u8) ![]const u8: Constructs a 200 OK JSON response.router.notFoundResponse() ![]const u8: Constructs a 404 Not Found response.Once you have a Router instance and defined your handler functions, you can register them with the router using the register method ([Ztack API Reference]):
app.register(method, path, handler):
method: An HttpMethod enum value (e.g., .GET, .POST).path: A []const u8 representing the URL path to match (e.g., /, /api/users).handler: A pointer to your handler function (e.g., &handleIndex).You can register multiple routes for different methods and paths. If a method/path combination is registered more than once, the behavior might depend on the internal implementation, but generally, you should register unique combinations.
Before a request can be dispatched, its method and path need to be extracted from the raw HTTP request line. Ztack provides a helper for this: Router.parseRequestLine.
router.parseRequestLine(request_line: []const u8) !?[]const []const u8: This function takes a []const u8 representing the first line of an HTTP request (e.g., "GET / HTTP/1.1").[]const []const u8. If successful, the array will contain two []const u8 slices:
parsed[0]: The HTTP method as a string (e.g., "GET").parsed[1]: The request path as a string (e.g., "/").method_str to the router.HttpMethod enum to pass it to the dispatch method.The dispatch method is where the router takes the parsed request information and finds the corresponding handler.
app.dispatch(method: HttpMethod, path: []const u8, body: []const u8) !?[]const u8:
method: The parsed HttpMethod of the request.path: The parsed URL path.body: The raw request body.dispatch method returns an optional []const u8.
[]const u8 generated by the handler (representing the complete HTTP response).null. Your server logic should then handle this case, typically by sending a 404 Not Found response using router.notFoundResponse().The following code snippet, adapted from the README, demonstrates how all these components work together in a basic Ztack server application. It shows the initialization, a handler, route registration, and the conceptual dispatch loop.
This example illustrates the complete flow from router initialization and route registration to request parsing and dispatch. In a real server, the "Conceptual Server Loop" would involve network I/O to read requests and send responses over TCP connections.
With a solid understanding of Ztack's routing capabilities, you can now:
src/examples/router_server.zig file for a fully functional, minimal routing server example.html.zig to build dynamic server-side rendered pages.