<skill_overview> Build robust, typed, and maintainable HTTP APIs with axum
Creating new axum endpoints Designing routers and route groups Using extractors for request data Sharing application state safely Implementing middleware and error mapping
Axum GitHub Axum Extractors Docs
</skill_overview>
Use Router::route with method routers for clarity Group related endpoints with nested routers Prefer typed Path and Query extractors over manual parsing Use fallback to handle unknown routes consistently
use axum::{routing::{get, post}, Router};
let app = Router::new() .route("/users", get(list_users).post(create_user)) .route("/users/{id}", get(get_user));
Compose multiple extractors in one handler Only one body-consuming extractor per handler Validate and deserialize input via extractors, not manually
Path for route params Query for query string Json for JSON payloads State for shared app state
async fn handler( State(state): State, Path(id): Path, Query(params): Query ) { /* ... */ }
<state_management>
Keep AppState cloneable and cheap to clone Store shared resources behind Arc Use State for app-level dependencies
#[derive(Clone)] struct AppState { pool: std::sync::Arc, }
</state_management>
Apply Router::layer for global middleware Use route_layer for per-route middleware Prefer explicit ordering of layers
use axum::{middleware, Router};
let app = Router::new() .route("/health", get(health)) .route_layer(middleware::from_fn(auth_middleware));
<error_handling>
Return Result from handlers and map errors to responses Implement IntoResponse for custom error types Never panic on expected errors
enum AppError { NotFound, BadRequest }
impl axum::response::IntoResponse for AppError { /* map to status */ }
async fn handler() -> Result<impl axum::response::IntoResponse, AppError> { /* ... */ }
</error_handling>