wstd is the Rust library that implements it — the only dependency a FastEdge app in Rust needs.
By the end of this guide:
- A Rust component compiles to WebAssembly and deploys to FastEdge
- Incoming HTTP requests are handled and a response is returned
- A second component calls an external REST API from inside the handler, transforms the response, and returns shaped JSON
Toolchain verification
This component reads the request URL and echoes it back: minimal code, but enough to exercise the complete build-upload-deploy pipeline.Project setup
Rust builds libraries withcargo new --lib. Before that project can compile to a WebAssembly component, two things need to be in place: a compilation target that tells Rust which platform to target, and a Cargo configuration that changes the output format to something the WASI runtime can load.
-
Add the
wasm32-wasip2compilation target — without it, Cargo can’t produce a WebAssembly component compatible with FastEdge. It’s a one-time step: -
Create the library crate:
-
By default,
cargo new --libproduces a Rustrlib— a format for linking into other Rust programs. FastEdge needs acdylibinstead: a C-compatible shared library that the WASI runtime can load as a component. OpenCargo.tomland replace its contents:The[package]and[dependencies]sections are standard Cargo. The only change from the default iscrate-type = ["cdylib"]in[lib]— without it, the build succeeds but the output can’t run as a WebAssembly component.
Handler
FastEdge calls a single function for every HTTP request — the component’s entry point. Inwstd, this is an async function marked with #[wstd::http_server]: the attribute tells the runtime which function to call, the function receives a Request<Body>, and it must return a Response<Body> wrapped in anyhow::Result.
cargo new --lib generates a default src/lib.rs with placeholder code — replace it with the actual handler:
Build
Compile the component to WebAssembly:./target/wasm32-wasip2/release/hello_world.wasm.
Deployment and testing
FastEdge separates binaries from apps: a binary is a compiled WebAssembly file stored on the platform, and an app is a named endpoint that references a binary. Deploying requires two API calls — upload the binary first to get an ID, then create the app using that ID. Both calls authenticate with a permanent API token, so set it before running the commands:id from the upload response to create the app. The binary ID and app URL in the examples below will differ from the ones returned by the upload:
Fetch data from an external API
The first component demonstrates the basic request-response cycle, but it only operates on the incoming request. WASI-HTTP’s key capability is that the handler can make outbound HTTP calls — reach out to any external service, transform the response, and return the result to the original caller. This component fetches a list of users from a public REST API and returns the first five as JSON.The examples use
jsonplaceholder.typicode.com, a free placeholder API, as a stand-in for any REST endpoint. It is suitable for development and testing only — do not use it in production.Project setup
Create a separate project for this component:serde_json for parsing the upstream API response. Replace Cargo.toml:
serde_json::Value — an untyped JSON tree — rather than defining structs. For slicing and reshaping an existing JSON response, this avoids deserializing into types that exist only to be re-serialized.
Handler
The handler follows the same request-response pattern as the first example, but adds an outbound HTTP call and JSON transformation. Replacesrc/lib.rs:
Client::new() creates a client that routes requests through the WASI outbound-http interface — the host runtime handles the actual network call. The await on client.send() is real async: the handler yields while the upstream request is in flight.
The response body arrives as a stream. upstream_resp.into_parts() separates the response metadata from that stream, and body.contents().await reads it fully into memory as a byte slice before parsing. This is the right approach when the upstream response is small enough to buffer; for large responses that only need to be forwarded without transformation, the stream can be passed through directly without loading it into memory first.
Every ? in the handler propagates errors back through anyhow::Result — FastEdge converts any handler returning Err into a 500 response, with the error message written to application logs rather than the response body. This means errors stay server-side and never leak to the caller.
Build
Compile the component using the same command as before:./target/wasm32-wasip2/release/outbound_fetch.wasm.
Deployment and testing
Upload and deploy using the same pattern as the first component:id and use it to create the app:
Cleanup
To delete an app, use itsid from the create-app response: