Web Frameworks

Libraries for building web applications and APIs.

axum

Modern, ergonomic web framework built on tokio.

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }

Basic Server

use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Hello, World!" }));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Handlers and Extractors

use axum::{
    extract::{Path, Query, State, Json},
    response::IntoResponse,
    http::StatusCode,
};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct Params {
    page: Option<u32>,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
}

// Path parameters
async fn get_user(Path(id): Path<u64>) -> impl IntoResponse {
    Json(User { id, name: "Alice".into() })
}

// Query parameters
async fn list_users(Query(params): Query<Params>) -> impl IntoResponse {
    let page = params.page.unwrap_or(1);
    format!("Page: {}", page)
}

// JSON body
async fn create_user(Json(user): Json<User>) -> impl IntoResponse {
    (StatusCode::CREATED, Json(user))
}

State and Middleware

use axum::{middleware, Extension};
use std::sync::Arc;

struct AppState {
    db: Pool,
}

async fn handler(State(state): State<Arc<AppState>>) -> String {
    // Use state.db
    "OK".into()
}

let app = Router::new()
    .route("/", get(handler))
    .with_state(Arc::new(AppState { db: pool }))
    .layer(middleware::from_fn(logging_middleware));

actix-web

High-performance, actor-based web framework.

[dependencies]
actix-web = "4"

Basic Server

use actix_web::{web, App, HttpServer, HttpResponse};

async fn hello() -> HttpResponse {
    HttpResponse::Ok().body("Hello!")
}

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

Handlers and Extractors

use actix_web::{web, HttpResponse};

#[derive(Deserialize)]
struct Info {
    name: String,
}

async fn greet(path: web::Path<String>) -> HttpResponse {
    HttpResponse::Ok().body(format!("Hello {}!", path))
}

async fn create(info: web::Json<Info>) -> HttpResponse {
    HttpResponse::Created().json(&*info)
}

async fn query(query: web::Query<Info>) -> HttpResponse {
    HttpResponse::Ok().body(format!("Name: {}", query.name))
}

App State

struct AppState {
    counter: std::sync::Mutex<i32>,
}

async fn increment(data: web::Data<AppState>) -> HttpResponse {
    let mut counter = data.counter.lock().unwrap();
    *counter += 1;
    HttpResponse::Ok().body(format!("Count: {}", counter))
}

let app = App::new()
    .app_data(web::Data::new(AppState {
        counter: std::sync::Mutex::new(0),
    }))
    .route("/", web::get().to(increment));

Rocket

Web framework focusing on ease of use.

[dependencies]
rocket = "0.5"

Basic Server

#[macro_use] extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

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

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

Request Guards and Forms

use rocket::form::Form;
use rocket::serde::json::Json;

#[derive(FromForm)]
struct Login {
    username: String,
    password: String,
}

#[post("/login", data = "<form>")]
fn login(form: Form<Login>) -> String {
    format!("Welcome, {}!", form.username)
}

#[post("/api/user", data = "<user>")]
fn create_user(user: Json<User>) -> Json<User> {
    user
}

warp

Composable web framework using filters.

[dependencies]
warp = "0.3"
tokio = { version = "1", features = ["full"] }

Basic Server

use warp::Filter;

#[tokio::main]
async fn main() {
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name));

    let routes = warp::get().and(hello);

    warp::serve(routes)
        .run(([127, 0, 0, 1], 3030))
        .await;
}

Filters and Combinators

use warp::Filter;

let api = warp::path("api");
let v1 = api.and(warp::path("v1"));

let users = v1
    .and(warp::path("users"))
    .and(warp::get())
    .and_then(list_users);

let create = v1
    .and(warp::path("users"))
    .and(warp::post())
    .and(warp::body::json())
    .and_then(create_user);

let routes = users.or(create);

Comparison

Framework Performance Ease of Use Flexibility
axum Excellent Good High
actix-web Excellent Good High
Rocket Good Excellent Medium
warp Excellent Medium High

Middleware Ecosystem

tower (for axum)

use tower_http::{cors::CorsLayer, trace::TraceLayer};

let app = Router::new()
    .route("/", get(handler))
    .layer(TraceLayer::new_for_http())
    .layer(CorsLayer::permissive());

Common Middleware

Crate Purpose
tower-http HTTP middleware
tower-cookies Cookie handling
axum-extra Additional extractors
actix-cors CORS for actix
actix-session Sessions for actix

Choosing a Framework

Use Case Recommendation
New projects axum
Maximum performance actix-web
Rapid development Rocket
Composable APIs warp

Summary

Framework Runtime Style
axum tokio Extractors, Router
actix-web actix Actors, Handlers
Rocket tokio Attributes, Guards
warp tokio Filters, Combinators

Back to top

Rust Programming Guide is not affiliated with the Rust Foundation. Content is provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.