| name | axum-routing-best-practices |
| description | Axum web framework routing patterns, path parameters, and common pitfalls. Essential for building HTTP APIs. Use when this capability is needed. |
| metadata | {"author":"fnsk4r17s"} |
Axum Routing Best Practices
This skill covers Axum routing patterns, focusing on common mistakes and breaking changes between versions.
Critical: Path Parameter Syntax (Axum 0.7+ → 0.8+)
⚠️ Breaking Change Alert
Axum 0.8+ changed path parameter syntax:
| Version | Syntax | Example |
|---|
| Axum 0.7 and earlier | :param | /users/:id |
| Axum 0.8+ | {param} | /users/{id} |
❌ WRONG (will panic at runtime)
.route("/users/:id", get(get_user))
.route("/specs/:name/tasks/:task_id", get(get_task))
Error message:
Path segments must not start with `:`. For capture groups, use `{capture}`.
If you meant to literally match a segment starting with a colon, call `without_v07_checks` on the router.
✅ CORRECT (Axum 0.8+)
.route("/users/{id}", get(get_user))
.route("/specs/{name}/tasks/{task_id}", get(get_task))
Why This Matters
- The panic happens at runtime when the router is constructed
- This means tests won't catch it unless they actually start the server
- The error message is helpful but the app crashes on startup
Route Definition Patterns
Basic Routes
use axum::{Router, routing::{get, post, put, delete, patch}};
pub fn routes() -> Router<AppState> {
Router::new()
.route("/health", get(health_check))
.route("/users", get(list_users).post(create_user))
.route("/users/{id}", get(get_user).put(update_user).delete(delete_user))
.route("/users/{user_id}/posts/{post_id}", get(get_user_post))
}
Extracting Path Parameters
use axum::extract::Path;
async fn get_user(Path(id): Path<String>) -> impl IntoResponse {
}
async fn get_user_post(
Path((user_id, post_id)): Path<(String, String)>
) -> impl IntoResponse {
}
#[derive(Deserialize)]
struct PostParams {
user_id: String,
post_id: String,
}
async fn get_user_post_v2(Path(params): Path<PostParams>) -> impl IntoResponse {
}
Route Organization
Module Pattern
mod users;
mod posts;
mod health;
pub fn create_router(state: AppState) -> Router {
Router::new()
.merge(users::routes())
.merge(posts::routes())
.merge(health::routes())
.with_state(state)
}
pub fn routes() -> Router<AppState> {
Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/{id}", get(get_user).delete(delete_user))
}
Nesting Routes
let app = Router::new()
.route("/health", get(health_check))
.nest("/api", api_router)
.with_state(state);
Response Patterns
Standard Response Pattern
use axum::{Json, response::IntoResponse};
use axum::http::StatusCode;
async fn get_user(
State(state): State<AppState>,
Path(id): Path<String>,
) -> impl IntoResponse {
match get_user_handler(&state, id).await {
Ok(user) => Json(user).into_response(),
Err(e) => e.into_response(),
}
}
async fn create_user(
State(state): State<AppState>,
Json(request): Json<CreateUserRequest>,
) -> impl IntoResponse {
match create_user_handler(&state, request).await {
Ok(user) => (StatusCode::CREATED, Json(user)).into_response(),
Err(e) => e.into_response(),
}
}
async fn delete_user(
State(state): State<AppState>,
Path(id): Path<>,
) {
(&state, id). {
(()) => StatusCode::NO_CONTENT.(),
(e) => e.(),
}
}
Common Mistakes
1. Route Order Matters
Router::new()
.route("/users/{id}", get(get_user))
.route("/users/me", get(get_current_user))
Router::new()
.route("/users/me", get(get_current_user))
.route("/users/{id}", get(get_user))
2. Forgetting State
pub fn routes() -> Router {
Router::new()
.route("/users", get(list_users))
}
pub fn routes() -> Router<AppState> {
Router::new()
.route("/users", get(list_users))
}
3. Conflicting Routes
Router::new()
.route("/users", get(list_users))
.route("/users", post(create_user))
Router::new()
.route("/users", get(list_users).post(create_user))
Quick Reference
HTTP Method Mapping
| Method | Axum Function | Typical Use |
|---|
| GET | get() | Fetch resource(s) |
| POST | post() | Create resource |
| PUT | put() | Replace resource |
| PATCH | patch() | Partial update |
| DELETE | delete() | Remove resource |
Path Parameter Syntax (Axum 0.8+)
| Pattern | Example URL | Extracted Value |
|---|
/{id} | /123 | "123" |
/{a}/{b} | /foo/bar | ("foo", "bar") |
/*path | /a/b/c | "a/b/c" (wildcard) |
Verification Checklist
Before committing Axum route changes:
Converted and distributed by TomeVault — claim your Tome and manage your conversions.