Shuttle-next

Shuttle-next is a brand new WASM web-framework based on axum and hyper. There are many benefits to using WASM for a backend web-framework, but one of the key features is the isolation it offers. Since WASM modules have no notion of a file system, a shuttle-next project can only interact with the host through resources explicitly declared by us. In this first iteration, the only resource available is an HTTP stream to and from a project.

Shuttle-next is very much a work in progress, and we’re releasing this iteration without some of the features that make shuttle a pleasure to use, for example the ability to effortlessly deploy and connect to a database. It’s also not currently possible to use any middleware. We’re planning to implement this functionality, and more, in the near future. We’re also hoping that the feedback we get from this release will help us see which features we should prioritize, and which areas of the framework’s design need the most attention going forward.

Note: shuttle-next does not currently work on Windows, it only works on unix systems (i.e mac, Linux and Windows Subsystem for Linux). We aim to resolve this in future releases.

Hello world!

Simple ‘Hello world’ app using shuttle-next.

cargo new --lib hello-world
cd hello-world

Make sure that your Cargo.toml file looks like the one below — having the right dependencies is key! You’ll also notice that this is a cdylib and not a binary, like other shuttle projects.

Cargo.toml
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = [ "cdylib" ]

[dependencies]
shuttle-next = "0.17.0"

Using the shuttle-next::app! macro, we can create HTTP API endpoints, setting the method and the route endpoint in the shuttle::endpoint() macro arguments. Any function that is a valid axum handler can be used, and you are free to add functions that aren’t annotated with the shuttle_next::endpoint macro and use them in your handlers. For now, everything has to go in the same file, inside the shuttle_next::app! macro.

lib.rs
shuttle_next::app! {
    #[shuttle_next::endpoint(method = get, route = "/hello")]
    async fn hello() -> &'static str {
        "Hello, World!"
    }
}

To test your app locally you’ll first need to add the wasm32-wasi target:

rustup target add wasm32-wasi

And we’re ready to start it up:

cargo shuttle run

Next, curl it to verify that it’s working:

curl localhost:8000/hello

We can also add an endpoint that takes the body from the request using the axum BodyStream extractor, lazily map its bytes to uppercase and stream it back in our response:

First, lets add the futures crate to our Cargo.toml:

cargo add futures
lib.rs
shuttle_next::app! {
    // We need to add some imports (inside the app! macro), 
    // all re-exported from axum 0.6
    use shuttle_next::body::StreamBody;
    use shuttle_next::extract::BodyStream;
    use shuttle_next::response::{Response, IntoResponse};
    
    // We'll also need the futures `TryStreamExt` trait to manipulate 
    // the body stream
    use futures::TryStreamExt;

    #[shuttle_next::endpoint(method = post, route = "/uppercase")]
    async fn uppercase(body: BodyStream) -> impl IntoResponse {
        let chunk_stream = body.map_ok(|chunk| {
            chunk
                .iter()
                .map(|byte| byte.to_ascii_uppercase())
                .collect::<Vec<u8>>()
        });
        Response::new(StreamBody::new(chunk_stream))
    }
}

Let’s run it again, and add a body to our request:

curl localhost:8000/uppercase -d "Please convert me to uppercase."

We can also add more handlers with the same route, granted they have different HTTP methods:

lib.rs
shuttle_next::app! {
    #[shuttle_next::endpoint(method = get, route = "/hello")]
    async fn get_hello() -> &'static str {
        "Hello from get_hello!"
    }

	#[shuttle_next::endpoint(method = post, route = "/hello")]
    async fn post_hello() -> &'static str {
        "Hello from post_hello!"
    }
}

Finally, to deploy your app, all you need to do is:

cargo shuttle project start

And then:

cargo shuttle deploy

And your app is live! 🎉🎉🎉