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.
Create a new Zig project:
zig init-exe
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.zigEdit the
src/main.zig
to match the following contents:worker.zigconst 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);
}Additionally, you can now go further add all the information from the received
worker.Request
:worker.zigconst 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);
}Compile the project
zig build-exe src/main.zig \
--name worker \
-mexec-model=reactor \
-target wasm32-wasi \
--mod worker::lib/worker.zig \
--deps workerYou can also use a build script to build the project with a simple
zig build
, please find some inspiration in our zig examples.Run your worker with
wws
. If you didn't download thewws
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:8080Finally, 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:
Create a new Zig project:
zig init-exe
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 ./libEdit
src/main.zig
file with the following contents:main.zigconst 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);
}Compile the project
zig build-exe src/main.zig \
--name worker-kv \
-mexec-model=reactor \
-target wasm32-wasi \
--mod worker::lib/worker.zig \
--deps workerYou can also use a build script to build the project with a simple
zig build
, please find some inspiration in our zig examples.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 haveworker-kv.wasm
andworker-kv.toml
in the same folder:worker-kv.tomlname = "workerkv"
version = "1"
[data]
[data.kv]
namespace = "workerkv"Run your worker with
wws
. If you didn't download thewws
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:8080Finally, 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:
Use the
worker.ParamsKey
context value to read in the passed in parameters:main.zigconst std = @import("std");
const worker = @import("worker");
fn requestFn(resp: *worker.Response, r: *worker.Request) void {
var params = r.context.params;
...
}Then, you can read the values as follows:
main.zigconst 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);
}Compile the project
zig build-exe src/main.zig \
--name "[id]" \
-mexec-model=reactor \
-target wasm32-wasi \
--mod worker::lib/worker.zig \
--deps workerYou can also use a build script to build the project with a simple
zig build
, please find some inspiration in our zig examples.Run your worker with
wws
. If you didn't download thewws
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:8080Finally, 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:
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:
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 Store | Environment Variables | Dynamic Routes | Folders | HTTP Requests |
---|---|---|---|---|
✅ | ✅ | ✅ | ✅ | ❌ |