Blog Bliki

Bliki (noun): like a blog and wiki in one. Posts in here will evolve over time.

Subscribe to the mailing list to get new blog posts and other things we think will be of interest straight to your inbox.

Choosing a Rust web framework

March 7, 2024 | Tim Abell | 7 minute read

Unlike in dotnet and ruby on rails where there is more or less ONE TRUE WAY™ to build web things there are several competing backend frameworks / crates for web backends in Rust. There are many choices for the front-end with and without Rust, but for this post I’m just going to look at backend options as we want to build something more interesting on the server than an single-page-application (SPA) or static site.

What we’re looking for in this post is a sensible default choice for the average API / web project. This post lays out the current lay of the land and proposes that with all the options available the Tokio “stack” is a good starting point.

Before we start looking at the actual web crates we need to talk a bit about async…

Async in Rust

In Rust there are multiple async runtime crates available.

Unlike C# where async has one language-supported way to do it that works out-of-the-box, the Rust language provides some of the pieces but to actually use async you’ll need an “async runtime” crate. (More info in “The Rust Book” section on async support.)

In Rust there are multiple async runtime crates available.

The crates that you are actually trying to use for your project might require a particular async runtime to work. As such your web framework and other crates that you use need to be compatible.

“After two decades of JavaScript and decent experience with Go, [async] is the most significant source of frustration and friction with Rust”

~ Jarrod Overson, “Was Rust Worth It? From JavaScript to Rust, three years in”

The consequence of this is that your up-front choice of async runtime will likely be hard to change further down the road. Your choice of web framework might in turn dictate a particular async runtime, so will also be hard to change.

Async and Performance

Coming from C# where async is embedded in the language and the standard “dotnet framework” libraries, you don’t really consider much whether to use async as it just infects everything you do for better or worse.

Here in Rust there is more choice. It’s worth noting that where async shines is in handling massive concurrent load on network servers where there’s a lot of waiting on IO (disk/network). For many applications however the concurrent load isn’t the limiting factor so async is potentially unnecessary complexity.

There’s a temptation to think “async” is a panacea for speed, but async doesn’t solve every performance problem. The main strength of async is in I/O blocked parallel operations, exactly the kind of thing webservers do (i.e. lots of waiting on disk and network while trying to serve thousands or millions of requests/clients in parallel).

To learn more about async in Rust the “Rust Async Book” is excellent.

So what are our options for async in Rust?

Available Async Runtimes

Here’s the main options for bringing the async features of the rust language to life. Without one of these you can use some of the async keywords but they won’t actually do anything.

Tokio

Tokio is far more than an async runtime. It has many of the things you’ll need to build a web API or host a dynamic server-rendered website. As such choosing Tokio for your async runtime implies a set of other choices (and vice versa if you choose other parts of the tokio stack, you’ll likely use the tokio async runtime.)

Tokio’s async is “A runtime for writing reliable network applications without compromising speed.”

Async-std

async-std is an “Async version of the Rust standard library”.

Perhaps better for smaller more focussed usages of async rather than “generic web development” where Tokio provides more out of the box.

Embassy

Embassy - “Embassy is the next-generation framework for embedded applications”.

Made for use in embedded / low power devices with more constraints than your average cloud web host. If you’re building something for the embedded space this is definitely one to check out.

Smol

Smol is declared to be “A small and fast async runtime.”

Seems worth a look if you want compactness over general applicability but doesn’t appear to have the crate ecosystem that Tokio benefits from.

Which to choose?

For the average web/API project Tokio seems like a good starting point, with a large ecosystem and many crates you can use with it.

Web & API frameworks & crates for the backend

Now that we’ve looked at the async runtimes and how it affects the rest of your choices, let’s have a look at what’s available for building server driven web applications and APIs in Rust.

Here’s the biggest frameworks / crates out there for doing server-side (aka backend) web development for both APIs and server-rendered html.

Available web frameworks

Axum

Axum by tokio - recommended, part of tokio (the async framework of choice) - focus on a macro free API which simplifies error handling and debugging

Actix

Actix - uses macros for routing API and the actor pattern for internal communication. Considered fast.

Rocket

Rocket - inconsistent development & releases, but otherwise good and solid according to blessed.rs. Uses macros to routing API and general web boilerplate. A popular choice if you don’t mind some magic happening behind the scenes.

Salvo

Salvo “A powerful and simple Rust web server framework”

Warp

Warp “A super-easy, composable, web server framework for warp speeds.”

Tide

Tide “a minimal and pragmatic Rust web application framework built for rapid development. It comes with a robust set of features that make building async web applications and APIs easier and more fun”

Poem

“Poem is a full-featured and easy-to-use web framework”

I don’t know much about this one yet, but a couple of people on reddit suggested this was nice to work with on the reddit responses to this post.

Hyper

“hyper is a fast and correct HTTP implementation”

Hyper provides and http server, but not a lot of the rest of the things you’d need for a full backend web application such as routing and templating.

Pavex

Currently in closed-access beta, this is an interesting looking new thing that promises to provide far better developer experience in the domain of backend web systems. Notably because of the pre-compilation parsing that allows it to provide programming error information to the programmer that explains the actual problem is within the context of backend development rather than some mysterious type/trait error. It also has a pretty clean design for describing your particular service.

You can watch Luca introduce and demonstrate Pavex at Rust Nation 2024.

Macros

Your choice may be affected by how you feel about macros - do you want to avoid magic and trickier error messages that you may get with a macro based framework, or are you happy to just write your business logic and leave all that to the framework.

Where to start

For “generic” backend web development it’s looks like Tokio is again a good default option.

Books

For more detail on many aspects of Rust for the web and much more you might find the survey of the available Rust books useful, which has a category specifically for web / API books.

Further Resources

Thanks

  • Many thanks to David Haig for early review and significant contributions to this article.
  • Many thanks to those on Reddit who responded with things I’d missed out.