Core API¶
Method¶
HTTP method enum with 7 variants: .get, .post, .put, .patch, .delete, .options, .head.
Methods¶
Method.fromStdString(m) Method— convert fromstd.http.Methodmethod.toStdString() std.http.Method— convert tostd.http.Method
SameSite¶
Cookie SameSite attribute enum:
- Strict — only sent for same-site requests
- Lax — sent for top-level navigations from other sites (default)
- None — sent for all requests (requires Secure flag)
Cookie¶
Configuration struct for Set-Cookie headers:
- name: []const u8 — cookie name
- value: []const u8 = "" — cookie value
- path: []const u8 = "/" — cookie path
- domain: ?[]const u8 = null — cookie domain
- max_age: ?u32 = null — max age in seconds
- secure: bool = false — only send over HTTPS
- http_only: bool = false — not accessible to JavaScript
- same_site: SameSite = .Lax — SameSite policy
Server¶
Low-level HTTP server. Binds to a host:port, accepts connections, parses HTTP, and dispatches to a handler.
Server.Config¶
host: []const u8 = "127.0.0.1"— bind addressport: u16 = 8080— listen portread_buffer_size: usize = 8192— read buffer sizewrite_buffer_size: usize = 8192— write buffer sizemax_body_size: usize = 10_485_760— max request body (10 MiB)max_inline_body_size: usize = 1_048_576— max body buffered before streaming (1 MiB); bodies larger than this are read viaRequest.body_streammax_requests: ?usize = null— optional request limit (for tests)
Server.HandlerFn¶
= *const fn (*Request, *Response) void— handler function signature (terminal handler does NOT receiveio)
ParsedTarget¶
Result of parsing a request target:
- path: []const u8 — path portion
- query: std.StringHashMap([]const u8) — parsed query parameters
Methods¶
Server.init(config: Config) Server— create a new server with the given configServer.listenAddress(host, port) !std.Io.net.IpAddress— parse host + port into an address (static)Server.parseRequestTarget(gpa, target) ParsedTarget— parse a request target into path and query (static)Server.buildRequest(gpa, head_buffer, max_body_size) !Request— build aRequestfrom raw HTTP head bytes; parses method, target, headers, validates content-length (static)server.listenAndServe(io, gpa, handler)— start listening and dispatching; accepts io and allocator; blocks until shutdown ormax_requestsreachedserver.shutdown(io)— set shutdown flag and close listener
Request Flow¶
listenAndServebinds the address and accepts connections in a loop- Each connection is parsed via
std.http.Server.receiveHead() buildRequestparses the head buffer into aRequeststruct- If the method expects a body, it reads it, then auto-parses
application/x-www-form-urlencodedandmultipart/form-data - The handler is called with
&req, &res Response.send()serializes the response back to the client
App¶
High-level application struct that wraps Server, handlers, router, middleware, and database.
Fields¶
server: Server— the underlying HTTP serverallocator: std.mem.Allocator— memory allocatorhandler_fn: ?Server.HandlerFn = null— terminal request handlerrouter_handler: ?Server.HandlerFn = null— router dispatch handler (takes priority overhandler_fn)middleware_handler: ?Server.HandlerFn = null— middleware pipeline handler (takes priority over all)db: ?*sqlite.Db = null— optional ORM database connection
Methods¶
App.init(gpa, config) App— create a new app with allocator and server configapp.deinit()— free resources (closes database if attached)app.handler(fn_ptr)— register a plain request handler (*const fn (*Request, *Response) void)app.routerHandler(fn_ptr)— register a router dispatch handler (takes priority over plain handler)app.database(db)— attach an ORM database connectionapp.middlewareHandler(fn_ptr)— register a middleware pipeline handler (takes priority over router and handler)app.buildRequestFromHead(head_buffer) !Request— parse an HTTP request head into aRequestapp.handleRequest(req, res)— dispatch through middleware → router → handler → default 404app.listenAndServe(io)— start the server; blocks until shutdown. Warns if no handler is registered.app.shutdown(io)— gracefully stop the server
Handler Priority¶
middleware_handler— if set, called first; responsible for calling the terminal handlerrouter_handler— called if no middleware handlerhandler_fn— called if no router handler- Default 404 — returned if nothing is registered
Request¶
Represents an incoming HTTP request.
Fields¶
method: Method— HTTP method enumpath: []const u8— raw request path (e.g."/users/42")query: std.StringHashMap([]const u8)— URL query string parametersheaders: std.StringHashMap([]const u8)— HTTP headersbody: []const u8— raw request bodybody_owned: bool = false— whether body was allocated (freed in deinit)query_owned: bool = false— whether query entries were allocated bydecodeUrlEncodedfiles: std.StringHashMap(FileUpload)— multipart uploaded files keyed by field namefiles_owned: bool = false— whether files has been initializedparams: RouteParams— route-extracted URL parameters (populated by Router.dispatch)allocator: std.mem.Allocator— allocator scoped to this requestuser: ?*anyopaque = null— optional authenticated user (set by session/auth middleware)body_stream: ?BodyStream = null— streaming body reader for bodies exceedingmax_inline_body_size
BodyStream¶
reader: *std.Io.Reader— the underlying readertotal_read: usize = 0— total bytes read so farstream.read(buf) !usize— read the next chunkstream.skip() !void— discard remaining body
FileUpload¶
filename: []const u8— client-provided filenamecontent_type: []const u8— part content type (empty string when omitted)data: []const u8— owned uploaded bytesfileUpload.deinit(gpa)— free all owned memory
MultipartForm¶
fields: std.StringHashMap([]const u8)— text form fieldsfiles: std.StringHashMap(FileUpload)— uploaded filesallocator: std.mem.Allocator— allocatormultipart.deinit()— free all resources
Instance Methods¶
req.header(name) ?[]const u8— case-insensitive header lookupreq.queryParam(name) ?[]const u8— query parameter lookupreq.formValue(name) ?[]const u8— form value lookup (same storage as query for URL-encoded bodies)req.file(name) ?FileUpload— multipart uploaded file lookup by field namereq.range() ?Range— parse HTTP Range headerreq.cookie(name) ?[]const u8— cookie lookup (parsesCookieheader)req.deinit()— free all owned memory (headers, body, query, files)
Static Methods¶
Request.parsePath(target) []const u8— extract path from request target (before?)Request.parseQueryString(gpa, raw) StringHashMap— parse URL query string into a map; decoded values are ownedRequest.deinitQueryString(map, gpa)— free all memory fromparseQueryStringRequest.parseFormUrlEncoded(gpa, body) StringHashMap— parseapplication/x-www-form-urlencodedbodyRequest.parseMultipartFormData(gpa, content_type, body) MultipartForm— parsemultipart/form-datainto text fields and filesRequest.deinitFiles(files, gpa)— free all memory from a file upload mapRequest.parseCookies(gpa, cookie_header) StringHashMap— parse cookie header into a mapRequest.getHeaderCI(headers, name) ?[]const u8— case-insensitive header lookup from any header mapRequest.validateBodySize(body_len, max) !void— validate body size against a maximum; returnserror.BodyTooLargeif exceeded
Auto-parsing¶
When the server receives a request with a body, it auto-detects the content type:
- application/x-www-form-urlencoded — parsed into req.query (and thus accessible via formValue)
- multipart/form-data — text fields go into req.query, files go into req.files
Response¶
Represents an outgoing HTTP response.
Fields¶
status_code: u16 = 200— HTTP status codereason_phrase: ?[]const u8 = "OK"— reason phrase (auto-set for known codes)headers: std.StringHashMap([]const u8)— response headers (owned)set_cookie_headers: std.ArrayList([]const u8)— rawSet-Cookieheader valuesbody: ?[]const u8 = null— response body (owned)use_chunked: bool = false— useTransfer-Encoding: chunkedfile_body: ?FileBody = null— streaming file descriptor (fd + size) for large payloadsallocator: std.mem.Allocator— memory allocator
Types¶
SameSiteenum:Strict,Lax,NoneCookiestruct: name, value, path, domain, max_age, secure, http_only, same_siteFileBodystruct:handle: std.Io.File.Handle,size: usize— file handle for streaming file responses
Lifecycle¶
Response.init(gpa) Response— create a new responseres.deinit()— free all owned memory (body, headers, cookie headers); the file body handle (if any) is closed bysend(), not bydeinit()
Chaining Mutators¶
All mutators return *Response for chaining unless they return an error union.
res.status(code) *Response— set HTTP status code; auto-sets reason phrase for known codes (200, 201, 204, 301, 302, 303, 307, 308, 400, 401, 403, 404, 405, 413, 422, 429, 500, 502, 503)res.header(name, value) *Response— set a response header; rejects CR/LF characters to prevent header injection; allocates owned copies of name and valueres.addSetCookie(value) *Response— add a rawSet-Cookieheader value; multipleSet-Cookieheaders are preserved
Body Writers¶
res.text(content) !void— set plain text body (Content-Type: text/plain; charset=utf-8)res.html(content) !void— set HTML body (Content-Type: text/html; charset=utf-8)res.json(content: anytype) !void— set JSON body; byte slices are treated as pre-serialized JSON; other types are serialized viastd.json.Stringify(Content-Type: application/json)res.stream(content) !void— set chunked streaming response (Transfer-Encoding: chunked,Content-Type: text/plain; charset=utf-8)res.redirect(url, code) !void— set redirect withLocationheader and status code
Cookie Helpers¶
res.setCookie(cookie: Cookie) *Response— add aSet-Cookieheader from aCookiestruct; builds the full cookie string withPath,Domain,Max-Age,Secure,HttpOnly,SameSiteattributesres.deleteCookie(name) *Response— delete a cookie by settingMax-Age=0
File Body¶
res.setFileBody(handle, size) !void— set a file handle as the response body for streaming; bypasses buffering entire payload in memory; the handle is automatically closed bysend()
Serialization¶
res.send(io, w: *std.Io.Writer) !void— serialize the full HTTP response (status line, headers, Set-Cookie headers, body) into the provided writer; iffile_bodyis set, streams file content directly to the writer and auto-closes the file handleres.sendHeaders(w: *std.Io.Writer) !void— serialize only the status line and headers (used when streaming file bodies manually)
Known Status Codes¶
200 OK, 201 Created, 204 No Content, 206 Partial Content, 301 Moved Permanently, 302 Found, 303 See Other, 304 Not Modified, 307 Temporary Redirect, 308 Permanent Redirect, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed, 413 Payload Too Large, 416 Range Not Satisfiable, 422 Unprocessable Entity, 429 Too Many Requests, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable
Full Example¶
const std = @import("std");
const zypher = @import("zypher");
pub fn main(init: std.process.Init) !void {
var app = zypher.core.App.init(init.gpa, .{ .port = 8080 });
app.handler(struct {
fn handle(req: *zypher.core.Request, res: *zypher.core.Response) void {
_ = req;
res.text("Hello, World!") catch {};
}
}.handle);
try app.listenAndServe(init.io);
}
IO Configuration¶
zypher provides a unified API for IO configuration through zypher.core.IoConfig. See io_migration.md for detailed documentation.
Quick Start with IO Config¶
const std = @import("std");
const zypher = @import("zypher");
pub fn main(init: std.process.Init) !void {
// Use default IO configuration (recommended)
const io_config = zypher.core.IoConfig.default();
const io = try io_config.createIo(init);
var app = zypher.core.App.init(init.gpa, .{ .port = 8080 });
defer app.deinit();
app.handler(myHandler);
try app.listenAndServe(io);
}