A simple framework for writing web services in zig.
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flopetautschnig 4708c03a48
Update README.md
1 year ago
src fix accept GET query string in URI 1 year ago
.gitignore update .gitignore 1 year ago
LICENSE Create LICENSE 1 year ago
README.md Update README.md 1 year ago
build.zig make lib and zig.mod 1 year ago
gyro.zzz update gyro.zzzz 1 year ago
zig.mod enable for lower than zig version 0.11.0 1 year ago



A simple framework for writing web services in zig.

Create a simple web app

const zrv = @import("zerve"); // Or set the path to zerve.zig e.g. @import("zerve-main/src/zerve.zig");
const Request = zrv.Request;
const Response = zrv.Response;
const Server = zrv.Server;
const Route = zrv.Route;
const allocator = std.heap.page_allocator; // Choose any allocator you want!

fn index(req: *Request) Response {
    return Response.write("hello!");

fn about(req: *Request) Response {
    return Response.write("about site");

fn writeJson(req: *Request) Response {
    return Response.json("[1, 2, 3, 4]");

pub fn main() !void {
     const rt = [_]Route{.{"/", index}, .{"/about", about}, .{"/json", writeJson}};

     try Server.listen("", 8080, &rt, allocator); // listens to http://localhost:8080
                                                         // http://localhost:8080/  "hello!"
                                                         // http://localhost:8080/about "about site"
                                                         // http://localhost:8080/json  "[1, 2, 3, 4]" (JSON-Response)



To write a web service with zerve you have to configure one or more Routes. They are being set by creating an Array of Route.


const rt = [_]Route{.{"/hello", helloFunction}, "/about", aboutFunction};

You can also set only one path and link it to a handler function, but since Server.listen() takes an Array of Route as one of it's arguments, you have do declare it as an Array as well:

const rt = [_]Route{.{"/hello", helloFunction}};

Handler Functions

Every Request is handled by a handler function. It has to be of this type: fn(req: *Request) Response


fn hello(req: *Request) Response {
    _ = req;
    return Response.write("hello"); // `Server` will return a Reponse with body "hello". You will see "hello" on your browser.


This represents the Request sent by the client.

pub const Request = struct {
    /// The Request Method, e.g. "GET"
    method: Method,
    /// HTTP-Version of the Request sent by the client
    httpVersion: HTTP_Version,
    /// Represents the request headers sent by the client
    headers: []const Header,
    /// The Request URI
    uri: []const u8,
    /// Represents the request body sent by the client
    body: []const u8,

Get Query Params

zerve lets you easily extract query params no matter if Requestmethod is GETor POST.

This can be done by using the getQuery method of Request.


fn index(req: Request) Response {

    // Assuming that a query string has been sent by the client containing the requested param,
    // e.g. `?user=james`

    const user = req.getQuery("user"); // This will return an optional
    if (user == null) return Response.write("") else return Response.write(user.?);

Get value of Request header by key

You can get the header value of any sent header by the client with the headermethod of Request.


fn index(req: *Request) Response {
    // Get value of the 'Content-Type' header

    const h = req.header("Content-Type"); // This will return an optional

    if (h == null) return Response.write("Header not found!") else return Response.write(h.?);



A Response that is sent ny the server. Every handler function has to return a Response.

pub const Response = struct {
    httpVersion: HTTP_Version = HTTP_Version.HTTP1_1,
    /// Response status, default is "200 OK"
    status: stat.Status = stat.Status.OK,
    /// Response eaders sent by the server
    headers: []const Header = &[_]Header{.{ .key = "Content-Type", .value = "text/html; charset=utf-8" }},
    /// Response body sent by the server
    body: []const u8 = "",

    /// Write a simple response.
    pub fn write(s: []const u8) Response

    /// Send a response with json content.
    pub fn json(j: []const u8) Response

    /// Send a response with status not found.
    pub fn notfound(s: []const u8) Response

    /// Send a response with status forbidden.
    pub fn forbidden(s: []u8) Response


Every Request or Response has Headers represented by an Array of Headers. Every Header has a key and a value.

pub const Header = struct {
    key: []const u8,
    value: []const u8,


To read the Cookie of a request by key, Request has a cookie-method. It returns an optional and fetches the value of a Request.Cookie.

Get Request Cookie value by key:

fn index(req: *Request) Response {
    // Fetches the cookie value by cookie name.
    // The `cookie` method will return an optional and will be `null`
    // in case that the cookie does not exist.

    const cookie = if (req.cookie("password")) |password| password else "";

    return Response.write("cookie-test");

To send a cookie in your Response just add a Response.Cookie to the cookies field. The cookies field is a slice of Response.Cookie.

fn index(_: *Request) Response {

    // Define a cookie with name and value.
    // It will live for 24 hours, since `maxAge` represents
    // lifetime in seconds.
    // See all field of the `Response.Cookie` struct below.

    const cookie = Response.Cookie{.name="User", .value="James", .maxAge=60*60*24};

    var res = Response.write("Set Cookie!");
    // add cookie to the `cookies` field which is a slice of `Response.Cookie`
    res.cookies = &[_]Response.Cookie{.{cookie}};
    return res;

This are the fields of Response.Cookie:

    name: []const u8,
    value: []const u8,
    path: []const u8 = "/",
    domain: []const u8 = "",
    /// Indicates the number of seconds until the cookie expires.
    maxAge: i64 = 0,
    secure: bool = true,
    httpOnly: bool = true,
    sameSite: SameSite = .lax,


Represents the http method of a Request or a Response.

pub const Method = enum {

    /// Turns the HTTP_method into a u8-Slice.
    pub fn stringify(m: Method) []const u8 {...}


The HTTP-Version of a Request or a Response.

pub const HTTP_Version = enum {

    /// Parses from `[]u8`
    pub fn parse(s: []const u8) HTTP_Version {...}

    /// Stringifies `HTTP_Version`
    pub fn stringify(version: HTTP_Version) []const u8 {...}




Server is a namespace to configure IP and Port the app will listen to by calling Server.listen(), as well as the routing paths ([]Route) it shall handle. You can also choose an allocator that the app will use for dynamic memory allocation.

pub fn listen(ip: []const u8, port: u16, rt: []const Route, allocator: std.mem.Allocator) !void {...}