Best Rust Web Frameworks – 2021

A List of currently working and maintained Rust web frameworks as of 2021

Rust is a programming language that’s focused on safety, speed, and concurrency. It offers all the performance and control of a low-level language, but with the powerful abstractions of a high-level language. It has be trending for te past couple of years among developers.

It's powerful, relatively simpler and is thoroughly documented. Lots and lots of web frameworks are also being developed and released and this tutorial aims to keep track of them.

Let's have a look at some of these frameworks.

NB/= This list is only ordered based on date we discovered these frameworks and not on their perceived rank.

(a). Actix Web

Actix is a powerful, pragmatic, and extremely fast web framework for Rust.

From a general perspective, here is a summary of why you should consider using Actix, it is:

Type Safe

In Actix everything has types.

Feature Rich

Actix provides a lot of features out of box. HTTP/2, logging, etc.

Extensible

With Actix, you can easily create your own libraries that can be shared with other developers.

Blazingly Fast

Actix is blazingly fast. See for yourself!.

This framework is thorughly documented and contains examples.

Here are some more framework features that you may find interesting:

Flexible Responders

Handler functions in actix can return a wide range of objects that implement the Responder trait. This makes it a breeze to return consistent responses from your APIs.

#[derive(Serialize)]
struct Measurement {
    temperature: f32,
}

async fn hello_world() -> impl Responder {
    "Hello World!"
}

async fn current_temperature() -> impl Responder {
    web::Json(Measurement { temperature: 42.3 })
}

Powerful Extractors

Actix comes with a powerful extractor system that extracts data from the incoming HTTP request and passes it to your view functions. Not only does this make for a convenient API but it also means that your view functions can be synchronous code and still benefit from asynchronous IO handling.

#[derive(Deserialize, Serialize)]
struct Event {
    id: Option<i32>,
    timestamp: f64,
    kind: String,
    tags: Vec<String>,
}

async fn capture_event(evt: web::Json<Event>) -> impl Responder {
    let new_event = store_in_db(evt.timestamp, &evt.kind, &evt.tags);
    format!("got event {}", new_event.id.unwrap())
}

Easy Form Handling

Handling multipart/urlencoded form data is easy. Just define a structure that can be deserialized and actix will handle the rest.

#[derive(Deserialize)]
struct Register {
    username: String,
    country: String,
}

async fn register(form: web::Form<Register>) -> impl Responder {
    format!("Hello {} from {}!", form.username, form.country)
}

Request Routing

An actix app comes with a URL routing system that lets you match on URLs and invoke individual handlers. For extra flexibility, scopes can be used.

#[get("/")]
async fn index(_req: HttpRequest) -> impl Responder {
    "Hello from the index page!"
}

async fn hello(path: web::Path<String>) -> impl Responder {
    format!("Hello {}!", &path)
}

let app = App::new()
    .service(index)
    .route("/{name}", web::get().to(hello));

Simple Example

Here is a simple Actix example to wet your appetite.

use actix_web::{web, App, HttpRequest, HttpServer, Responder};

async fn greet(req: HttpRequest) -> impl Responder {
    let name = req.match_info().get("name").unwrap_or("World");
    format!("Hello {}!", &name)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(greet))
            .route("/{name}", web::get().to(greet))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Links

Here are the links to Actix:

Number Link
1. Home
2. Documentation
3. Github

(b). Rocket

Rocket is a web framework for Rust that makes it simple to write fast, secure web applications without sacrificing flexibility, usability, or type safety.

Here are it's main features:

Type Safe

From request to response Rocket ensures that your types mean something.

Boilerplate Free

With Rocket, you spend your time writing code that really matters, it can generate the rest for you.

Easy To Use

Simple, intuitive APIs make Rocket approachable, no matter your background.

Extensible

With Rocketm you can create your own first-class primitives that any Rocket application can use.

Interesting Feature

Here are some more framework features you may like:

Forms? Check!

Handling forms is simple and easy. Simply derive FromForm for your structure and let Rocket know which parameter to use. Rocket parses and validates the form request, creates the structure, and calls your function.

Bad form request? Rocket doesn’t call your function! What if you want to know if the form was bad? Simple! Change the type of task to Option or Result!

#[derive(FromForm)]
struct Task<'r> {
   description: &'r str,
   completed: bool
}

#[post("/", data = "<task>")]
fn new(task: Form<Task<'_>>) -> Flash<Redirect> {
    if task.description.is_empty() {
        Flash::error(Redirect::to(uri!(home)), "Cannot be empty.")
    } else {
        Flash::success(Redirect::to(uri!(home)), "Task added.")
    }
}

JSON, out of the box.

Rocket has first-class support for JSON, right out of the box. Simply derive Deserialize or Serialize to receive or return JSON, respectively.

Like other important features, JSON works through Rocket’s FromData trait, Rocket’s approach to deriving types from body data. It works like this: specify a data route parameter of any type that implements FromData. A value of that type will then be created automatically from the incoming request body. Best of all, you can implement FromData for your types!

#[derive(Serialize, Deserialize)]
struct Message<'r> {
   contents: &'r str,
}

#[put("/<id>", data = "<msg>")]
fn update(db: &Db, id: Id, msg: Json<Message<'_>>) -> JsonValue {
    if db.contains_key(&id) {
        db.insert(id, msg.contents);
        json!({ "status": "ok" })
    } else {
        json!({ "status": "error" })
    }
}

Templating

Rocket makes templating a breeze with built-in templating support.

Cookies

View, add, or remove cookies, with or without encryption, without hassle.

Async Streams

Create and return potentially infinite async streams of data with ease.

Config Profiles

Configure your application your way for debug, release, or anything else!

Testing Library

Unit test your applications with ease using the built-in testing library.

Typed URIs

Rocket typechecks route URIs for you so you never mistype a URI again.

Simple Example

Here's a simple hello world in Rocket:

#[macro_use] extern crate rocket;

#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![hello])
}

If you run http://localhost:8000/hello/John/58 in your server you would see something like this in your browser:

Hello, 58 year old named John!

Links

Here are the links:

Number Link
1. Home
2. Documentation
3. Github

(c). Gotham

A flexible web framework that promotes stability, safety, security and speed.

So why should you consider using Gotham. Well here are the reasons:

Stability Focused

Gotham, according to it's developers, only targets stable Rust. Gotham is also automatically tested against Rust beta and nightly.

No Garbage Collection

One of Rust’s key innovations is guaranteeing memory safety without requiring garbage collection. Gotham based applications automatically benefit from the predictability and performance of a system without garbage collection.

Statically Typed

Gotham is statically typed ensuring your application is correctly expressed at compile time.

Async Everything

By leveraging Tokio, Gotham types are async out of the box.

Gotham's async story is further enhanced by Hyper, a fast server that provides an elegant layer over stringly typed HTTP.

Blazingly Fast

Measure completed requests, including the 99th percentile, in µs.

Simple Example

Here is a simple hello world program written in Gotham:

//! A Hello World example application for working with Gotham.

use gotham::state::State;

const HELLO_WORLD: &str = "Hello World!";

/// Create a `Handler` which is invoked when responding to a `Request`.
///
/// How does a function become a `Handler`?.
/// We've simply implemented the `Handler` trait, for functions that match the signature used here,
/// within Gotham itself.
pub fn say_hello(state: State) -> (State, &'static str) {
    (state, HELLO_WORLD)
}

/// Start a server and call the `Handler` we've defined above for each `Request` we receive.
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, || Ok(say_hello))
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;

    #[test]
    fn receive_hello_world_response() {
        let test_server = TestServer::new(|| Ok(say_hello)).unwrap();
        let response = test_server
            .client()
            .get("http://localhost")
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        let body = response.read_body().unwrap();
        assert_eq!(&body[..], b"Hello World!");
    }
}

Links

Here are the links for Gotham:

Number Link
1. Home
2. Documentation
3. Github

Leave a Reply

Your email address will not be published. Required fields are marked *