Wasm Labs @ VMware OCTO

Run your workers anywhere with WebAssembly

By Angel M Miguel
At 2022 / 10 15 mins reading

Workers are an exciting, relatively recent technology to run serverless applications, typically in the Edge. This article covers Wasm Workers Server, an open source project to run workers-based applications: scripts or functions that receive an HTTP request and return an HTTP response. Workers are a way to encapsulate application logic in a modular, self-contained and portable way.

Workers started on the browser to run code in a separate thread from the main one. More recently, companies like Deno, Cloudflare, and Vercel have popularized server-side workers on their edge networks thanks to new isolation technologies. It brings the power of serverless close to your users.

Some of these companies are behind the WinterCG, a working group that aims to create a spec for implementing Web Platform APIs like workers outside of browsers. Other platforms like Fermyon, Suborbital and wasmCloud, follow a similar approach in their implementations.

WebAssembly (Wasm) fits perfectly with the workers mindset due to its properties: self-contained, isolated, polyglot, portable, and secure. However, writing any code that involves Wasm is not an easy task. From when you write the initial code until you see the result, there are many steps you need to complete: download the tooling, compile the source code and sometimes even compile interpreters. And then, repeat these steps per every module.

Writing any code that involves Wasm is not an easy task. From when you write the initial code until you see the result, there are many steps you need to complete.

The next challenge is to connect your new shiny Wasm module to the world. You need to configure a self-hosted platform or publish your module in an existing public one. You may find yourself spinning up new infrastructure, modifying an existing one, or creating new accounts.

What if you are experimenting and do not want to go that far yet? What if you can download a binary and start serving your Wasm modules directly? What if you can run JavaScript workers in WebAssembly without having to compile them?

We are excited to share Wasm Workers Server with you. wws is an OSS implementation that allows you to run workers in a variety of environments that you control, from your laptop to your own server or a tiny Raspberry Pi. Everything in a self-contained binary.

Wasm Workers Server

Wasm Workers Server (wws) is a self-contained server that looks for Wasm and compatible modules (like JavaScript files) in your filesystem and configures different HTTP endpoints to serve them. The server will route any HTTP request to your workers and reply with the returned response.

All workers run as Wasm modules inside the Wasmtime runtime. Even JavaScript source code is sent to a compiled JavaScript interpreter running inside Wasmtime. wws brings you all the benefits of WebAssembly isolation and compatibility with existing workers in a single binary.

Our main goals are: simplicity and compatibility. Write and run your first handler in 5 minutes. Move them to compatible platforms as your application grows.

The HTTP endpoints are calculated based on your current filesystem. wws automatically detects the JavaScript and Wasm workers in the folder and sets the routes to serve them. No configuration file is required. We are following the same approach as tremendously successful projects like NextJS and Astro. It removes any initial friction of writing configuration files. You will write them only when it's required.

See this example of a folder with different workers in it:

$ tree .
.
├── api
│ └── hello.js
└── index.wasm

$ wws .
⚙️ Loading routes from: .
🗺 Detected routes:
- http://127.0.0.1:8080/
=> index.wasm (handler: default)
- http://127.0.0.1:8080/api/hello
=> api/hello.js (handler: default)
🚀 Start serving requests at http://127.0.0.1:8080

In addition to that, wws includes other features like an in-memory Key / Value store. This data layer allows you to write more complete applications. For example, we created a Tic Tac Toe game you can play with friends.

A page showing a Tic Tac Toe game implemented with Wasm Workers Server

Getting Started

To start working with wws, you can run our installer for Unix systems:

curl https://raw.githubusercontent.com/vmware-labs/wasm-workers-server/main/install.sh | bash && \
wws --help

By default, the script installs wws globally in your system. It may require sudo permissions in some operating systems like macOS. If you prefer to download wws in your current folder and try it first, run the install script with the --local flag:

curl https://raw.githubusercontent.com/vmware-labs/wasm-workers-server/main/install.sh | bash -s -- --local && \
./wws --help

Before running wws, you need to place a worker that can reply to HTTP requests. For now, let's use one of the examples in our repository:

curl https://raw.githubusercontent.com/vmware-labs/wasm-workers-server/main/examples/js-basic/handler.js \
-o ./index.js

Now, you can run wws in your current folder and see how it looks for compatible workers:

# Use "./wws ." if you installed the server locally
wws .

⚙️ Loading routes from: .
🗺 Detected routes:
- http://127.0.0.1:8080/
=> index.js (handler: default)
🚀 Start serving requests at http://127.0.0.1:8080

Finally, access http://127.0.0.1:8080 in your browser to see the result.

Create your first worker

As mentioned earlier, workers are functions that receive a request and return a response. One of the main goals of Wasm Workers Server is compatibility. We want to work with standards and provide a seamless experience to create and migrate workers between platforms and tools that follow this approach.

For this reason, wws implements the core concepts of this specification. Wasm and JavaScript workers follow this approach, making it easier to run them on different platforms with minimal changes in the code.

We are still working on the API, so some features are not available in wws yet. Feel free to play with it and create an issue if you miss any relevant ones.

Create a JavaScript worker

JavaScript workers are running in WebAssembly thanks to QuickJS. wws integrates with QuickJS to run your JavaScript code in WebAssembly, so you don't need to compile your source code to WebAssembly.

JavaScript workers use the Request/ Response classes. A worker needs to listen to the "fetch" event, which will be triggered on every request. The handler function receives the request and generates a response to reply to it.

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

  1. First, create a new index.js file with the following content. It is the minimum code you need to subscribe to the fetch event and return a valid response.

    const reply = (request) => {
    return new Response("Hello Wasm!");
    }

    // Subscribe to the Fetch event
    addEventListener("fetch", event => {
    return event.respondWith(reply(event.request));
    });
  2. Now, you can add more content to the reply function to show the request information. In addition to that, let's add a response header.

    const reply = (request) => {
    // Body response
    const body = `<!DOCTYPE html>
    <body>
    <h1>Hello from Wasm Workers Server</h1>
    <p>Replying to
    ${request.url}</p>
    <p>Method:
    ${request.method}</p>
    <p>User Agent:
    ${request.headers.get("userAgent")}</p>
    <p>Payload:
    ${request.body || "-"}</p>
    <p>
    This page was generated by a JavaScript file inside WebAssembly
    </p>
    </body>
    `
    ;

    // Build a new response
    let response = new Response(body);

    // Add a new header
    response.headers.set("x-generated-by", "wasm-workers-server");

    return response;
    }

    // Subscribe to the Fetch event
    addEventListener("fetch", event => {
    return event.respondWith(reply(event.request));
    });
  3. Save the file and run your worker with wws.

    wws .

    ⚙️ Loading routes from: .
    🗺 Detected routes:
    - http://127.0.0.1:8080/
    => index.js (handler: default)
    🚀 Start serving requests at http://127.0.0.1:8080
  4. Finally, open http://127.0.0.1:8080 in your browser.

Under the hood, wws is getting the JavaScript source code and passing it to the embedded QuickJS Wasm module. This module evaluates the code and provides the response back to wws. If you want to know more about the QuickJS Wasm module, check the code in our repository.

Create a Rust worker

To create your first Rust worker, please refer to our documentation site. This tutorial will help you to build the same page with Rust.

Other examples

You can find more guides and examples in the repository and documentation:

Run anywhere

Tools and platforms are part of any developer's life. We want to develop and publish our projects, and this process involves multiple decisions. With wws, we want to give you the flexibility you need. Migrating workers from and to wws should not be a complicated process.

That is the reason we chose the "Request / Response" approach. Any platform or tool that follows this ongoing standard should be able to reuse the workers' code with minimal changes. We believe this flexibility is critical in an ecosystem that is still evolving.

We believe this flexibility is critical in an ecosystem that is still evolving.

Here is where WebAssembly enters the scene. In the end, all workers are running in a WebAssembly runtime. That brings a lot of compiled and interpreted languages into the game. If a platform does not support a language and includes a Wasm runtime, you can rely on it to reuse the workers you create for wws.

The same happens in the opposite direction. If you create a worker in a language wws does not support by default, you can compile it to Wasm. It involves different knowledge, so we will keep this door open and ensure you do not need to use it that much.

It simply works

Wasm Workers Server simplifies how you develop and publish applications based on workers. Download the binary, write a worker, and start serving it. From zero to Wasm workers in less than 5 minutes.

From zero to Wasm workers in less than 5 minutes

Then, you can think about where to publish your application. As a single binary, wws can run in a container, in a VM, or directly in a host. Thanks to the low resource requirements, you can run in the free tier of many different cloud providers. In other words, you can reuse your current infrastructure or try it for free.

We are excited to share this project and looking forward to seeing the applications you build on top of it. Do not forget to follow us on @vmwwasm and share with us any project you build with Wasm Workers Server. We will love to see them 😊.

Do you want to stay up to date with WebAssembly and our projects?