Routes and Extractors

Axum in twenty lines, and the trait that makes handlers work.

Part 1 of 3Updated
cargo add axum tokio --features tokio/full
cargo add serde --features derive
use axum::{routing::get, Json, Router};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/health", get(|| async { "ok" }))
        .route("/tasks", get(list_tasks).post(create_task));

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

async fn create_task(Json(payload): Json<NewTask>) -> Json<Task> { /* ... */ }

The magic is FromRequest: any handler argument implementing it is extracted automatically. Json<T> deserializes the body, Path<T> pulls URL segments, Query<T> parses the query string. Order matters — a body-consuming extractor must come last, because there’s only one body.

When a handler doesn’t compile, the error is usually enormous and usually means one argument doesn’t implement FromRequest. Read the last line of the error first.