Wasm Workers Server 1.4, now with fetch support
Wasm Workers Server (wws) allows you to develop serverless applications in many programming languages such as JavaScript, Python and Go. You can even combine them to power your next app. Everything runs on WebAssembly, creating a safe and isolated environment for your workers.
The sandbox doesn’t allow you to access any resource by default. However, applications often need access to external resources like other services or APIs. For this reason, we will be introducing a new set of methods (bindings) that extend the capabilities of your workers.
The first one is the HTTP bindings that allow you to perform HTTP requests from inside your worker. You can start using it from v1.4 onwards.
Welcome the HTTP bindings
The new HTTP bindings introduce new functions and types that allow you to perform HTTP requests from your workers. You can access these functions through idiomatic APIs for each of the supported languages.
To start using it, download or update your Wasm Workers Server binary with the following command:
curl -fsSL https://workers.wasmlabs.dev/install | bash
Let's try it
In the following example, the worker retrieves the list of posts from the great {JSON} Placeholder API and displays them as a list. For this example, we will use JavaScript. You can also try the HTTP feature in Rust and Go. Ruby and Python support is under active development and will come soon.
In JavaScript workers, you can perform HTTP requests using the well-known fetch API. We chose this API as it is the default way to send HTTP requests in the JavaScript ecosystem and belongs to the WinterCG set of APIs.
To develop this worker, create an index.js
file with the following content:
/**
* Builds a reply to the given request
*/
const reply = async (request) => {
// Body response
let body;
try {
let res = await fetch('https://jsonplaceholder.typicode.com/posts/');
let json = await res.json();
// Build a new response
body = `<!DOCTYPE html>
<head>
<title>Posts</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>
<p>Available articles:</p>
<ul>
${json.map(({ title }) => `<li>${title.replace("<", "<").replace(">", ">")}</li>`).join("")}
</ul>
<p>
This page was generated by a JavaScript file running in WebAssembly.
</p>
</main>
</body>`;
} catch (e) {
body = `There was an error with the request: ${e}`;
}
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));
});
The worker code is functional but will fail if you try to run it. The reason is that Wasm Workers Server follows a capability-based model. Accessing external resources requires granting explicit permission. Create an index.toml
worker configuration file and set the allowed hosts to enable this feature:
name = "fetch"
version = "1"
[features]
[features.http_requests]
allowed_hosts = ["jsonplaceholder.typicode.com"]
Now, you can run the project with Wasm Workers Server:
$ wws .
⚙️ Preparing the project from: .
⚙️ Loading routes from: .
⏳ Loading workers from 1 routes...
✅ Workers loaded in 2.988781958s.
- http://127.0.0.1:8080/
=> ./index.js
🚀 Start serving requests at http://127.0.0.1:8080
Finally, access the site on http://localhost:8080.
Use the APIs you are familiar with
Similarly to JavaScript, you can access the HTTP feature from your Go and Rust workers. In these cases, the different kits expose a method that receives and returns common HTTP structs in both languages:
- Rust: expose a
send_http_request
method that receives an http::Request instance and returns an http::Response. - Go: expose a
SendHttpRequest
method that receives an http.Request instance and returns an http.Response.
You have all the information and more examples in the documentation:
The internals
If you are curious about the internals of this feature, this section gives you an overview of the implementation. Since Wasm Workers Server supports multiple languages, one of the main goals was to rely on other tools to simplify the integration and reuse as much code as possible.
The Component-Model and the WebAssembly Interface Types (WIT) allowed us to define the different types and methods and generate the code for Rust and Go. Most Wasm Workers Server kits use Rust to initialize internal runtimes like QuickJS, so we were able to reuse the same approach for the JavaScript kit.
You can find all the WIT files that define the HTTP Types and the bindings in the repository. Then, we generated the code for Rust and Go using the wit-bindgen project.
The process was simple, but we had to fit different pieces together. Wasm Workers Server uses Wasmtime as its Wasm runtime. The latest Wasmtime integration with WIT only works for components. Wasm Workers Server is on the way to adopt Wasm components, but it is not there yet.
We had to use a wit-bindgen backport maintained by the Fermyon Team. Once Wasm Workers Server fully supports Wasm components, it will update to the latest versions of these projects.
More to come
The HTTP bindings are the first step to introduce new features to the project. Now, Wasm Workers Server has the tools to start adding more methods to expand the possibilities of your workers and applications.
Following the capability-based approach, your workers won't be able to access any resource by default. This approach allows fine-tuning the worker permissions to reduce the attack surface and increase security.
Your workers won't be able to access any resource by default. This approach allows fine-tuning the worker permissions to reduce the attack surface and increase security.
If you are developing some cool apps with wws
, we would love to learn more about your use case. We are eager to see what you are building! You can find us on Twitter and in our GitHub repository ⭐️.