Rust & Wasm: Create Server-Side Apps

Nikhil Gupta
2 min readApr 8, 2023

Until now, we have created and deployed WASM modules in a browser, CLI and Python. In this article, we will instead create a web server completely in Rust, compile it to WASM and run it using WasmEdge.

Create a TCP Listener

Let’s first create a basic rust project

cargo new wasmedge-demo

and add hyper_wasi (for http server) and tokio_wasi (for async processors) in Cargo.toml like so:

[dependencies]
+hyper_wasi = { version = "0.15", features = ["full"]}
+tokio_wasi = { version = "1", features = ["rt", "macros", "net", "time", "io-util"]}

Next, let’s modify our lib.rs to create an async main that creates a TCP connection listener:

use std::net::SocketAddr;
use tokio::net::TcpListener;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));

let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);

Ok(())
}

This essentially creates a server on http://0.0.0.0 (or localhost) at port 8080.

Handle Connections

Now, let’s add the ability to handle new connections:

+use hyper::server::conn::Http;
+use hyper::service::service_fn;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));

let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
+ loop {
+ let (stream, _) = listener.accept().await?;
+
+ tokio::task::spawn(async move {
+ if let Err(err) = Http::new().serve_connection(stream, service_fn(handle_req)).await { // `handle_req` will be defined later
+ println!("Error serving connection: {:?}", err);
+ }
+ });
+ }
-
- Ok(())
}

This essentially waits for a connection, launches a new thread to process the requests and loops to start to wait for another connection.

Process Requests

Now, let’s define our handle_req function that handles any request to the given connection:

+use hyper::{Body, Request, Response};

+async fn handle_req(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
+ Ok(Response::new(Body::from(
+ "Hello World!",
+ )))
+}

This handler just responds with Hello World! for any request to the server.

Launch Server

To launch our server, let’s first install WasmEdge:

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash

Next, let’s build our app for wasm32-wasi:

cargo build --target wasm32-wasi --release

Finally, let’s run our server:

wasmedge target/wasm32-wasi/release/wasmedge-demo.wasm

You should see Listening on http://0.0.0.0:8080 on the console.

Now, our server is ready to serve requests. You can open the link on the browser and see Hello World! :)

If you liked this article, subscribe here to get the complete code and updates for the entire collection:

--

--