Skip to main content

Zig

Zig workers are tested with Zig version 0.11.0. Then, they are loaded by Wasm Workers Server and start processing requests.

Your first Zig worker

The recommended way to implement workers is by using the worker.ServeFunc function.

In this example, the worker will get a request and print all the related information.

  1. Create a new Zig project:

    zig init-exe
  2. Add Wasm Workers Server Zig dependency

    At this point in time Zigs Package manager is not yet available. We will therefore clone the repository to make the library locally available.

    mkdir lib
    wget -O ./lib/worker.zig https://raw.githubusercontent.com/vmware-labs/wasm-workers-server/main/kits/zig/worker/worker.zig
  3. Edit the src/main.zig to match the following contents:

    worker.zig
    const std = @import("std");
    const worker = @import("worker");

    fn requestFn(resp: *worker.Response, r: *worker.Request) void {
    _ = r;

    _ = &resp.headers.append("x-generated-by", "wasm-workers-server");
    _ = &resp.writeAll("hello from zig");
    }

    pub fn main() !void {
    worker.ServeFunc(requestFn);
    }
  4. Additionally, you can now go further add all the information from the received worker.Request:

    worker.zig
    const std = @import("std");
    const worker = @import("worker");

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();

    fn requestFn(resp: *worker.Response, r: *worker.Request) void {
    std.debug.print("Hello from function\n", .{ });

    // // TODO: prepare to read request body and send it back
    std.debug.print("+++ doing payload \n", .{ });

    var payload: []const u8 = "";
    var reqBody = r.data;

    if (reqBody.len == 0) {
    payload = "-";
    } else {
    payload = reqBody;
    }

    const s =
    \\<!DOCTYPE html>
    \\<head>
    \\<title>Wasm Workers Server</title>
    \\<meta name="viewport" content="width=device-width,initial-scale=1">
    \\<meta charset="UTF-8">
    \\<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
    \\<style>
    \\body {{ max-width: 1000px; }}
    \\main {{ margin: 5rem 0; }}
    \\h1, p {{ text-align: center; }}
    \\h1 {{ margin-bottom: 2rem; }}
    \\pre {{ font-size: .9rem; }}
    \\pre > code {{ padding: 2rem; }}
    \\p {{ margin-top: 2rem; }}
    \\</style>
    \\</head>
    \\<body>
    \\<main>
    \\<h1>Hello from Wasm Workers Server 👋</h1>
    \\<pre><code>Replying to {s}
    \\Method: {s}
    \\User Agent: {s}
    \\Payload: {s}</code></pre>
    \\<p>
    \\This page was generated by a Zig⚡️ file running in WebAssembly.
    \\</p>
    \\</main>
    \\</body>
    ;

    var body = std.fmt.allocPrint(allocator, s, .{ r.url.path, r.method, "-", payload }) catch undefined;

    _ = &resp.headers.append("x-generated-by", "wasm-workers-server");
    _ = &resp.writeAll(body);
    }

    pub fn main() !void {
    worker.ServeFunc(requestFn);
    }
  5. Compile the project

    zig build-exe src/main.zig \
    --name worker \
    -mexec-model=reactor \
    -target wasm32-wasi \
    --mod worker::lib/worker.zig \
    --deps worker

    You can also use a build script to build the project with a simple zig build, please find some inspiration in our zig examples.

  6. Run your worker with wws. If you didn't download the wws server yet, check our Getting Started guide.

    wws .

    ⚙️ Loading routes from: .
    🗺 Detected routes:
    - http://127.0.0.1:8080/worker
    => worker.wasm (name: default)
    🚀 Start serving requests at http://127.0.0.1:8080
  7. Finally, open http://127.0.0.1:8080/worker in your browser.

Add a Key / Value store

Wasm Workers allows you to add a Key / Value store to your workers. Read more information about this feature in the Key / Value store section.

To add a KV store to your worker, follow these steps:

  1. Create a new Zig project:

    zig init-exe
  2. Add Wasm Workers Server Zig dependency

    At this point in time Zigs Package manager is not yet available. We will therefore clone the repository to make the library locally available.

    mkdir lib
    wget -O ./lib/worker.zig https://raw.githubusercontent.com/vmware-labs/wasm-workers-server/main/kits/zig/worker/worker.zig ./lib
  3. Edit src/main.zig file with the following contents:

    main.zig
    const std = @import("std");
    const worker = @import("worker");

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();

    fn requestFn(resp: *worker.Response, r: *worker.Request) void {
    var cache = r.context.cache;
    var counter: i32 = 0;

    var v = cache.getOrPut("counter") catch undefined;

    if (!v.found_existing) {
    v.value_ptr.* = "0";
    } else {
    var counterValue = v.value_ptr.*;
    var num = std.fmt.parseInt(i32, counterValue, 10) catch undefined;
    counter = num + 1;
    var num_s = std.fmt.allocPrint(allocator, "{d}", .{ counter }) catch undefined;
    _ = cache.put("counter", num_s) catch undefined;
    }

    const s =
    \\<!DOCTYPE html>
    \\<head>
    \\<title>
    \\Wasm Workers Server - KV example</title>
    \\<meta name="viewport" content="width=device-width,initial-scale=1">
    \\<meta charset="UTF-8">
    \\</head>
    \\<body>
    \\<h1>Key / Value store in Zig</h1>
    \\<p>Counter: {d}</p>
    \\<p>This page was generated by a Zig⚡️ file running in WebAssembly.</p>
    \\</body>
    ;

    var body = std.fmt.allocPrint(allocator, s, .{ counter }) catch undefined; // add useragent

    _ = &resp.headers.append("x-generated-by", "wasm-workers-server");
    _ = &resp.writeAll(body);
    }

    pub fn main() !void {
    worker.ServeFunc(requestFn);
    }
  4. Compile the project

    zig build-exe src/main.zig \
    --name worker-kv \
    -mexec-model=reactor \
    -target wasm32-wasi \
    --mod worker::lib/worker.zig \
    --deps worker

    You can also use a build script to build the project with a simple zig build, please find some inspiration in our zig examples.

  5. Create a worker-kv.toml file with the following content. Note the name of the TOML file must match the name of the worker. In this case we have worker-kv.wasm and worker-kv.toml in the same folder:

    worker-kv.toml
    name = "workerkv"
    version = "1"

    [data]
    [data.kv]
    namespace = "workerkv"
  6. Run your worker with wws. If you didn't download the wws server yet, check our Getting Started guide.

    wws .

    ⚙️ Loading routes from: .
    🗺 Detected routes:
    - http://127.0.0.1:8080/worker-kv
    => worker-kv.wasm (name: default)
    🚀 Start serving requests at http://127.0.0.1:8080
  7. Finally, open http://127.0.0.1:8080/worker-kv in your browser.

Dynamic routes

You can define dynamic routes by adding route parameters to your worker files (like [id].wasm). To read them in Zig, follow these steps:

  1. Use the worker.ParamsKey context value to read in the passed in parameters:

    main.zig
    const std = @import("std");
    const worker = @import("worker");

    fn requestFn(resp: *worker.Response, r: *worker.Request) void {
    var params = r.context.params;

    ...
    }
  2. Then, you can read the values as follows:

    main.zig
    const std = @import("std");
    const worker = @import("worker");

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();

    fn requestFn(resp: *worker.Response, r: *worker.Request) void {
    var params = r.context.params;

    var id: []const u8 = "the value is not available";

    var v = params.get("id");

    if (v) |val| {
    id = val;
    }

    const s =
    \\Hey! The parameter is: {s}
    ;

    var body = std.fmt.allocPrint(allocator, s, .{ id }) catch undefined; // add useragent

    _ = &resp.headers.append("x-generated-by", "wasm-workers-server");
    _ = &resp.writeAll(body);
    }

    pub fn main() !void {
    worker.ServeFunc(requestFn);
    }
  3. Compile the project

    zig build-exe src/main.zig \
    --name "[id]" \
    -mexec-model=reactor \
    -target wasm32-wasi \
    --mod worker::lib/worker.zig \
    --deps worker

    You can also use a build script to build the project with a simple zig build, please find some inspiration in our zig examples.

  4. Run your worker with wws. If you didn't download the wws server yet, check our Getting Started guide.

    wws .

    ⚙️ Loading routes from: .
    🗺 Detected routes:
    - http://127.0.0.1:8080/[id]
    => worker-kv.wasm (name: default)
    🚀 Start serving requests at http://127.0.0.1:8080
  5. Finally, open http://127.0.0.1:8080/hello in your browser.

Read environment variables

Environment variables are configured via the related TOML configuration file. These variables are accessible via std.process.getEnvMap or std.process.getEnvVarOwned in your worker. To read them, just use the same name you configured in your TOML file:

envs.toml
name = "envs"
version = "1"

[vars]
MESSAGE = "Hello 👋! This message comes from an environment variable"

Now, you can read the MESSAGE variable using either the std.process.getEnvMap or the std.process.getEnvVarOwned functions:

envs.zig
const std = @import("std");
const worker = @import("worker");

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = arena.allocator();

fn requestFn(resp: *worker.Response, r: *worker.Request) void {
_ = r;

const envvar = std.process.getEnvVarOwned(allocator, "MESSAGE") catch "";
defer allocator.free(envvar);

const s =
\\The environment variable value is: {s}
;

var body = std.fmt.allocPrint(allocator, s, .{ envvar }) catch undefined; // add useragent

_ = &resp.headers.append("x-generated-by", "wasm-workers-server");
_ = &resp.writeAll(body);
}

pub fn main() !void {
worker.ServeFunc(requestFn);
}

If you prefer, you can configure the environment variable value dynamically by following these instructions.

Other examples

Find other examples in the /examples directory of wasm-workers-server repository.

Contributors

The Zig kit was originally authored by Christoph Voigt (@voigt).

Feature compatibility

Workers' features that are available in Zig:

K/V StoreEnvironment VariablesDynamic RoutesFoldersHTTP Requests