Rust
Rust workers are compiled into a Wasm module. Then, they are loaded by Wasm Workers Server and start processing requests.
Your first Rust worker
Every worker receives a Request<String> struct and returns a Response<Content>. These structs come from the widely known http
crate and the Content
struct is defined in our rust kit. It allows you returning different types. Finally, the worker
macro connects your worker with wws
.
In this example, the worker will get a request and print all the related information.
Create a new Rust project:
cargo new --name worker worker
Add the dependencies to the
Cargo.toml
file:Cargo.toml[package]
name = "worker"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.63"
wasm-workers-rs = { git = "https://github.com/vmware-labs/wasm-workers-server/" }Add the
reply
function to thesrc/main.rs
file. You will need to import the required resources from thewasm-workers-rs
crate and use theworker
macro:src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker]
fn reply(req: Request<String>) -> Result<Response<Content>> {
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(String::from("Hello wasm!").into())?)
}Now, you can add all the information from the given
Request
struct:src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, HeaderValue, Request, Response},
};
#[worker]
fn reply(req: Request<String>) -> Result<Response<String>> {
// Applied changes here to use the Response method. This requires changes
// on signature and how it returns the data.
let response = format!(
"<!DOCTYPE html>
<body>
<h1>Hello World</h1>
<p>Replying to {}</p>
<p>Method: {}</p>
<p>User Agent: {}</p>
<p>Body: {}</p>
<p>This page was generated by a Wasm modules built from Rust.</p>
</body>",
req.uri(),
req.method().as_str(),
req.headers()
.get("user-agent")
.unwrap_or(&HeaderValue::from_str("None").unwrap())
.to_str()
.unwrap(),
req.body()
);
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(response.into())?)
}In this case, you need to compile the project to Wasm (WASI):
# Install the component and build
rustup target add wasm32-wasi && \
cargo build --release --target wasm32-wasiRun your worker with
wws
. If you didn't download thewws
server yet, check our Getting Started guide.cd target/wasm32-wasi/release && \
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 Rust project:
cargo new --name worker-kv worker-kv
Add the dependencies to the
Cargo.toml
file:Cargo.toml[package]
name = "worker-kv"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.63"
wasm-workers-rs = { git = "https://github.com/vmware-labs/wasm-workers-server/" }Add the
reply
function to thesrc/main.rs
file. You will need to import the required resources from thewasm-workers-rs
crate and use theworker
macro. In this case, we will add a new attribute to theworker
macro calledcache
and update the function signature:src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker(cache)]
fn reply(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(String::from("Hello wasm!").into())?)
}Then, let's read a value from the cache and update it:
src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Cache, Content,
};
#[worker(cache)]
fn reply(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
// Applied changes here to use the Response method. This requires changes
// on signature and how it returns the data.
let count = cache.get("counter");
let count_num = match count {
Some(count_str) => count_str.parse::<u32>().unwrap_or(0),
None => 0,
};
let response = format!(
"<!DOCTYPE html>
<body>
<h1>Key / Value store in Rust</h1>
<p>Counter: {}</p>
<p>This page was generated by a Wasm modules built from Rust.</p>
</body>",
count_num
);
cache.insert("counter".to_string(), (count_num + 1).to_string());
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(response.into())?)
}Compile the project to Wasm (WASI):
# Install the component and build
rustup target add wasm32-wasi && \
cargo build --release --target wasm32-wasiCreate 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 (target/wasm32-wasi/release
):target/wasm32-wasi/release/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.cd target/wasm32-wasi/release && \
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 Rust, follow these steps:
Add the
params
configuration parameter to theworker
macro and update the method signature to receive the values:src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker(params)]
fn reply(req: Request<String>, params: &HashMap<String, String>) -> Result<Response<Content>> {
// ...
}Then, you can read the values from the
params
argument:src/main.rsuse anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker(params)]
fn reply(req: Request<String>, params: &HashMap<String, String>) -> Result<Response<Content>> {
let missing_param = String::from("none");
let id = params.get("id").unwrap_or_else(|| &missing_param);
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(format!("Hey! The parameter is: {}", id).into())?)
}
Read environment variables
Environment variables are configured via the related TOML configuration file. These variables are accessible via std::env
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 the std::env
Rust library:
use anyhow::Result;
use std::env;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker]
fn handler(req: Request<String>) -> Result<Response<Content>> {
// Read the environment variable using the std::env::var method
let message = env::var("MESSAGE").unwrap_or_else(|_| String::from("Missing message"));
let response = format!(
"The message is: {}",
message,
);
Ok(http::Response::builder()
.status(200)
.body(response.into())?)
}
If you prefer, you can configure the environment variable value dynamically by following these instructions.