{"id":13740941,"url":"https://github.com/floscodes/zerve","last_synced_at":"2025-12-30T06:06:49.146Z","repository":{"id":154169819,"uuid":"626539696","full_name":"floscodes/zerve","owner":"floscodes","description":"A simple framework for writing web services in zig.","archived":false,"fork":false,"pushed_at":"2024-06-12T04:47:06.000Z","size":71,"stargazers_count":54,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-06T09:47:59.293Z","etag":null,"topics":["framework","http-server","server","server-side","web"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/floscodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-11T17:09:06.000Z","updated_at":"2025-04-20T04:24:16.000Z","dependencies_parsed_at":"2024-01-27T09:38:59.548Z","dependency_job_id":"73f75147-769e-47a4-aef0-30672a88e11f","html_url":"https://github.com/floscodes/zerve","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floscodes%2Fzerve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floscodes%2Fzerve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floscodes%2Fzerve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floscodes%2Fzerve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/floscodes","download_url":"https://codeload.github.com/floscodes/zerve/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253145113,"owners_count":21861188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","http-server","server","server-side","web"],"created_at":"2024-08-03T04:00:53.818Z","updated_at":"2025-12-30T06:06:49.141Z","avatar_url":"https://github.com/floscodes.png","language":"Zig","readme":"# zerve\r\n\r\nA simple framework for writing web services in zig.\r\n\r\n* [Create a simple Web App](#create-a-simple-web-app)\r\n* [Types](#types)\r\n    * [Route](#route)\r\n    * [Handler Functions](#handler-functions)\r\n    * [Request](#request)\r\n        * [Get Query Params](#get-query-params)\r\n        * [Get Header Value by Key](#get-value-of-request-header-by-key)\r\n    * [Response](#response)\r\n    * [Header](#header)\r\n    * [Cookies](#cookies)\r\n        * [Read Cookie from Request](#read-cookie-from-request)\r\n        * [Add Cookie to Response](#add-cookie-to-response)\r\n    * [Method](#method)\r\n    * [HTTP-Version](#http-version)\r\n * [Namespaces](#namespaces)\r\n     * [Server](#server)\r\n\r\n# Create a simple web app\r\n\r\n```zig\r\nconst zrv = @import(\"zerve\"); // Or set the path to zerve.zig e.g. @import(\"zerve-main/src/zerve.zig\");\r\nconst Request = zrv.Request;\r\nconst Response = zrv.Response;\r\nconst Server = zrv.Server;\r\nconst Route = zrv.Route;\r\nconst allocator = std.heap.page_allocator; // Choose any allocator you want!\r\n\r\nfn index(req: *Request) Response {\r\n    _=req;\r\n    return Response.write(\"hello!\");\r\n}\r\n\r\nfn about(req: *Request) Response {\r\n    _=req;\r\n    return Response.write(\"about site\");\r\n}\r\n\r\nfn writeJson(req: *Request) Response {\r\n    _=req;\r\n    return Response.json(\"[1, 2, 3, 4]\");\r\n}\r\n\r\npub fn main() !void {\r\n     const rt = [_]Route{.{\"/\", index}, .{\"/about\", about}, .{\"/json\", writeJson}};\r\n\r\n     try Server.listen(\"0.0.0.0\", 8080, \u0026rt, allocator); // listens to http://localhost:8080\r\n                                                         // http://localhost:8080/  \"hello!\"\r\n                                                         // http://localhost:8080/about \"about site\"\r\n                                                         // http://localhost:8080/json  \"[1, 2, 3, 4]\" (JSON-Response)\r\n}\r\n```\r\n\r\n# Types\r\n\r\n## Route\r\n\r\nTo write a web service with **zerve** you have to configure one or more Routes. They are being set by creating an Array of `Route`.\r\n\r\nExample:\r\n```zig\r\nconst rt = [_]Route{.{\"/hello\", helloFunction}, \"/about\", aboutFunction};\r\n```\r\nYou 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,\r\nyou have do declare it as an Array as well:\r\n```zig\r\nconst rt = [_]Route{.{\"/hello\", helloFunction}};\r\n```\r\n\r\n## Handler Functions\r\n\r\nEvery Request is handled by a handler function. It has to be of this type: `fn(req: *Request) Response`\r\n\r\nExample:\r\n```zig\r\nfn hello(req: *Request) Response {\r\n    _ = req;\r\n    return Response.write(\"hello\"); // `Server` will return a Reponse with body \"hello\". You will see \"hello\" on your browser.\r\n}\r\n```\r\n\r\n## Request\r\n\r\nThis represents the Request sent by the client.\r\n```zig\r\npub const Request = struct {\r\n    /// The Request Method, e.g. \"GET\"\r\n    method: Method,\r\n    /// HTTP-Version of the Request sent by the client\r\n    httpVersion: HTTP_Version,\r\n    /// Represents the request headers sent by the client\r\n    headers: []const Header,\r\n    /// The Request URI\r\n    uri: []const u8,\r\n    /// Represents the request body sent by the client\r\n    body: []const u8,\r\n};\r\n```\r\n\r\n### Get Query Params\r\n\r\n**zerve** lets you easily extract query params no matter if `Request`method is `GET`or `POST`.\r\n\r\nThis can be done by using the `getQuery` method of `Request`.\r\n\r\nExample:\r\n```zig\r\nfn index(req: Request) Response {\r\n\r\n    // Assuming that a query string has been sent by the client containing the requested param,\r\n    // e.g. `?user=james`\r\n\r\n    const user = req.getQuery(\"user\"); // This will return an optional\r\n    \r\n    if (user == null) return Response.write(\"\") else return Response.write(user.?);\r\n    \r\n}\r\n```\r\n\r\n### Get value of Request header by key\r\n\r\nYou can get the header value of any sent header by the client with the `header`method of `Request`.\r\n\r\nExample:\r\n\r\n```zig\r\nfn index(req: *Request) Response {\r\n    \r\n    // Get value of the 'Content-Type' header\r\n\r\n    const h = req.header(\"Content-Type\"); // This will return an optional\r\n\r\n    if (h == null) return Response.write(\"Header not found!\") else return Response.write(h.?);\r\n\r\n}\r\n```\r\n\r\n## Response\r\n\r\nA Response that is sent ny the server. Every handler function has to return a `Response`.\r\n```zig\r\npub const Response = struct {\r\n    httpVersion: HTTP_Version = HTTP_Version.HTTP1_1,\r\n    /// Response status, default is \"200 OK\"\r\n    status: stat.Status = stat.Status.OK,\r\n    /// Response eaders sent by the server\r\n    headers: []const Header = \u0026[_]Header{.{ .key = \"Content-Type\", .value = \"text/html; charset=utf-8\" }},\r\n    /// Response body sent by the server\r\n    body: []const u8 = \"\",\r\n\r\n    /// Write a simple response.\r\n    pub fn write(s: []const u8) Response\r\n\r\n    /// Send a response with json content.\r\n    pub fn json(j: []const u8) Response\r\n\r\n    /// Send a response with status not found.\r\n    pub fn notfound(s: []const u8) Response\r\n\r\n    /// Send a response with status forbidden.\r\n    pub fn forbidden(s: []u8) Response\r\n};\r\n```\r\n\r\n## Header\r\n\r\nEvery Request or Response has Headers represented by an Array of Headers. Every Header has a key and a value.\r\n```zig\r\npub const Header = struct {\r\n    key: []const u8,\r\n    value: []const u8,\r\n};\r\n```\r\n\r\n## Cookies\r\n\r\n### Read Cookie from Request\r\n\r\nTo read the Cookie of a request by key, `Request` has a `cookie`-method.\r\nIt returns an optional and fetches the value of a `Request.Cookie`.\r\n\r\nGet Request Cookie value by key:\r\n```zig\r\nfn index(req: *Request) Response {\r\n    \r\n    // Fetches the cookie value by cookie name.\r\n    // The `cookie` method will return an optional and will be `null`\r\n    // in case that the cookie does not exist.\r\n\r\n    const cookie = if (req.cookie(\"password\")) |password| password else \"\";\r\n\r\n    return Response.write(\"cookie-test\");\r\n}\r\n```\r\n\r\n### Add Cookie to Response\r\n\r\nTo send a cookie in your `Response` just add a `Response.Cookie` to the `cookies` field.\r\nThe `cookies` field is a slice of `Response.Cookie`.\r\n\r\n```zig\r\nfn index(_: *Request) Response {\r\n\r\n    // Define a cookie with name and value.\r\n    // It will live for 24 hours, since `maxAge` represents\r\n    // lifetime in seconds.\r\n    // See all field of the `Response.Cookie` struct below.\r\n\r\n    const cookie = Response.Cookie{.name=\"User\", .value=\"James\", .maxAge=60*60*24};\r\n\r\n    var res = Response.write(\"Set Cookie!\");\r\n    // add cookie to the `cookies` field which is a slice of `Response.Cookie`\r\n    res.cookies = \u0026[_]Response.Cookie{.{cookie}};\r\n    \r\n    return res;\r\n}\r\n```\r\n\r\nThis are the fields of `Response.Cookie`:\r\n\r\n```zig\r\n    name: []const u8,\r\n    value: []const u8,\r\n    path: []const u8 = \"/\",\r\n    domain: []const u8 = \"\",\r\n    /// Indicates the number of seconds until the cookie expires.\r\n    maxAge: i64 = 0,\r\n    secure: bool = true,\r\n    httpOnly: bool = true,\r\n    sameSite: SameSite = .lax,\r\n```\r\n\r\n## Method\r\n\r\nRepresents the http method of a Request or a Response.\r\n```zig\r\npub const Method = enum {\r\n    GET,\r\n    POST,\r\n    PUT,\r\n    HEAD,\r\n    DELETE,\r\n    CONNECT,\r\n    OPTIONS,\r\n    TRACE,\r\n    PATCH,\r\n    UNKNOWN,\r\n\r\n    /// Turns the HTTP_method into a u8-Slice.\r\n    pub fn stringify(m: Method) []const u8 {...}\r\n};\r\n```\r\n\r\n## HTTP-Version\r\n\r\nThe HTTP-Version of a Request or a Response.\r\n```zig\r\npub const HTTP_Version = enum {\r\n    HTTP1_1,\r\n    HTTP2,\r\n\r\n    /// Parses from `[]u8`\r\n    pub fn parse(s: []const u8) HTTP_Version {...}\r\n\r\n    /// Stringifies `HTTP_Version`\r\n    pub fn stringify(version: HTTP_Version) []const u8 {...}\r\n\r\n};\r\n```\r\n\r\n# Namespaces\r\n\r\n## Server\r\n\r\nServer 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.\r\nYou can also choose an allocator that the app will use for dynamic memory allocation.\r\n```zig\r\npub fn listen(ip: []const u8, port: u16, rt: []const Route, allocator: std.mem.Allocator) !void {...}\r\n```\r\n","funding_links":[],"categories":["Zig","Libraries","Network \u0026 Web"],"sub_categories":["Web Framework"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloscodes%2Fzerve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffloscodes%2Fzerve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloscodes%2Fzerve/lists"}