A simple framework for writing web services in zig. https://github.com/floscodes/zerve
Find a file
2023-05-16 11:13:00 +02:00
src add comments 2023-05-16 11:13:00 +02:00
.gitignore adapt handle function type 2023-05-02 21:48:18 +02:00
build.zig make lib and zig.mod 2023-04-23 14:12:56 +02:00
gyro.zzz modifying gyro.zzz 2023-04-25 11:40:15 +02:00
LICENSE Create LICENSE 2023-04-24 12:13:17 +02:00
README.md Update README.md 2023-05-04 14:03:49 +02:00
zig.mod enable for lower than zig version 0.11.0 2023-05-02 22:24:25 +02:00

zerve

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 {
    _=req;
    return Response.write("hello!");
}

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

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

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

     try Server.listen("0.0.0.0", 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)
}

Types

Route

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.

Example:

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

Example:

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.
}

Request

This is 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,
};

Response

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 new(s: []const u8) Response {
        return Response{ .body = s };
    }

    /// Send a response with json content.
    pub fn json(j: []const u8) Response {
        return Response{ .headers = &[_]Header{.{ .key = "Content-Type", .value = "application/json" }}, .body = j };
    }

    /// Send a response with status not found.
    pub fn notfound(s: []const u8) Response {
        return Response{ .status = stat.Status.NOT_FOUND, .body = s };
    }

    /// Send a response with status forbidden.
    pub fn forbidden(s: []u8) Response {
        return Response{ .status = stat.Status.FORBIDDEN, .body = s };
    }
};

Header

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,
};

Method

Represents the http method of a Request or a Response.

pub const Method = enum {
    GET,
    POST,
    PUT,
    HEAD,
    DELETE,
    CONNECT,
    OPTIONS,
    TRACE,
    PATCH,
    UNKNOWN,

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

HTTP-Version

The HTTP-Version of a Request or a Response.

pub const HTTP_Version = enum {
    HTTP1_1,
    HTTP2,

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

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

};

Namespaces

Server

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 {...}