We use cookies to understand how the site is used and to display ads. Analytics and advertising only run after you accept. You can change your choice anytime. Privacy policy

Skip to content
devvkit
$devvkit learn --librarie axum-guide

Axum Guide

[rust][web][http]
Rust
Install
cargo add axum tokio --features tokio/full
cargo add serde serde_json

Axum is the official web framework from the Tokio team. Built on Tower middleware stack.

Extractors (Path, Query, Json) type-check request data at compile time.

Shared state via Extension or State extractor. Tower middleware layers for auth, logging, CORS.

Setup

Basic router· Hello world server.
use axum::{Router, routing::get};

async fn root() -> &'static str { "Hello" }

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(root));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Extractors

Path params· Capture URL segments.
use axum::extract::Path;

async fn user(Path(id): Path<u64>) -> String {
    format!("User {id}")
}
Query params· Parse query string.
use serde::Deserialize;
use axum::extract::Query;

#[derive(Deserialize)]
struct Pagination { page: u32, limit: u32 }

async fn list(Query(p): Query<Pagination>) -> String {
    format!("page={}, limit={}", p.page, p.limit)
}
JSON body· Parse JSON request.
use axum::extract::Json;

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

async fn create(Json(payload): Json<CreateUser>) -> Json<serde_json::Value> {
    Json(serde_json::json!({ "id": 1, "name": payload.name }))
}
Combined extractors· Path + Query + Json.
async fn update(
    Path(id): Path<u64>,
    Query(params): Query<UpdateParams>,
    Json(body): Json<UpdateBody>,
) -> impl IntoResponse { /* ... */ }

Middleware

Middleware· Tower layer.
use tower_http::trace::TraceLayer;

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

State

Shared state· Pass state to handlers.
use std::sync::Arc;

struct AppState { db: DbPool }

let state = Arc::new(AppState { db });
let app = Router::new()
    .route("/", get(handler))
    .with_state(state);

async fn handler(State(state): State<Arc<AppState>>) -> String {
    // use state.db
}