Repository navigation
v0.8.0
Zefir 0.8.0 adds zefir-http, an HTTP server with routes checked at compile time.
http
A service is made of controllers: structs whose methods handle requests and which declare their routes. The compiler knows every route of the service: it checks each handler's signature, and every error a handler may return, when it builds the service. What a request brings comes into structs of your own by the field_rules of zefir-validator:
// The body of POST: the title is trimmed, then checked.
const NewTodo = struct {
title: []const u8,
pub const field_rules = .{ .title = .{ v.trim, v.notBlank, v.maxLength(200) } };
};
const TodosV1Controller = struct {
pub const http_routes = http.routes(.{
.prefix = "/v1/todos",
.methods = .{
.list = http.get("", .{}),
.create = http.post("", .{}),
},
});
pub fn create(self: *TodosV1Controller, ctx: *http.Context) !http.Json(Todo) {
// Parsed, trimmed and checked: a body that does not fit never gets here.
const body = try ctx.json(NewTodo);
// ...
return .{ .value = todo, .status = 201 };
}
};A body that does not fit is a 422 in application/problem+json that names every field.
- Routes:
http.get,http.post,http.put,http.patch,http.delete,http.head,http.optionsandhttp.sse.get, with path parameters and catch-alls; 404, 405 withAllow; every GET answers HEAD too, and an OPTIONS request gets 204 with the methods of its path inAllow. - Input:
ctx.path,ctx.query,ctx.form,ctx.headers,ctx.cookiesandctx.json(T)read into your structs with their outside names, transforms and rules, and report every violation at once. - Output:
http.Json(T)by the same names and transforms, text and HTML, cookies, redirects, files and streamed responses. - One error handler turns every error into a response, problem+json by default; the compiler checks that every error a handler may return becomes a response.
- Middleware of your own, on the server, the controller and the route, in the order you write them;
onResponseandonFinishphases, and typedctx.locals. - Static files from a safe root,
http.static: MIME types,ETagandLast-Modifiedwith 304, ranges, and a fallback for single-page apps. - Server-sent events,
http.sse.Manager: subscriptions by key, heartbeats,Last-Event-IDand bounded queues. - Each request runs in a task of its own: a deadline gives 504, a client that leaves cancels its request,
.max_in_flightgives 503, and a stop waits.shutdown_gracefor the requests in flight. - Two backends:
http.native, on the network overstd.Io, on zio in a service; andhttp.testing, the same server in memory for tests, with no network. - A strict HTTP/1.1 parser of its own (RFC 9112), with limits on heads, targets and bodies,
Expect: 100-continue, pipelining and HTTP/1.0.
Not in this version yet, as the TODO list at the top of the guide says: WebSocket, templates, multipart/form-data uploads, OpenAPI, http.Patch(T), ready-made middleware, HTTP/2, HTTP/3 and TLS, compression.
On a shared GitHub runner, 4 vCPUs of an AMD EPYC 7763, a server on zio with four executors answers plain text at about 122 000 requests per second over 64 connections kept alive; the guide's benchmark shows the whole run.
See the http guide: a todo service built step by step, every example compiled and every run shown as it printed. For a first look, the README has http in a minute.
zig fetch --save git+https://github.com/vkiryakov/zefir#v0.8.0