
Axum
- 353 installs
- 63 repo stars
- Updated July 18, 2026
- bobmatnyc/claude-mpm-skills
axum is a Claude Code skill that teaches production Rust HTTP API patterns with Axum routers, typed extractors, Tower middleware, shared state, and structured error handling for developers building async backend services
About
axum is a toolchain skill (version 1.1.1) from bobmatnyc/claude-mpm-skills documenting production Axum patterns on Hyper and Tower. It walks through Router composition, Path/Query/Json/State extractors, Arc-based AppState, IntoResponse error types, tracing and timeout layers, graceful shutdown, and tower::ServiceExt router tests. Progressive disclosure loads roughly 140 entry tokens or about 5500 full tokens. Reach for axum when scaffolding Rust microservices, internal APIs, or replacing blocking handlers in an async Tokio service. The parent repository bundles 171 Claude Code skills including 6 Rust toolchain skills covering Axum, Tauri, and Clap.
- async routing
- typed extractors
- tower middleware
- shared application state
- structured error responses
Axum by the numbers
- 353 all-time installs (skills.sh)
- Ranked #1,188 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 1, 2026 (Skillselion catalog sync)
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill axumAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 353 |
|---|---|
| repo stars | ★ 63 |
| Last updated | July 18, 2026 |
| Repository | bobmatnyc/claude-mpm-skills ↗ |
How do you structure Axum routers for production Rust APIs?
Implement async Rust HTTP APIs with Axum routing, typed extractors, middleware, shared state, and structured error handling for production services.
Who is it for?
Rust backend developers shipping async HTTP APIs who want typed extractors, Tower middleware composition, and testable router patterns without reinventing production defaults.
Skip if: Developers building only frontend apps, Python/Node services, or Rust CLI tools that do not expose HTTP endpoints.
When should I use this skill?
The user is building or refactoring a Rust HTTP API with Axum, Tokio, or Tower and needs routing, middleware, state, error handling, or shutdown guidance.
What you get
Production-ready Axum Router layouts, typed handler patterns, middleware stacks, AppState definitions, structured error responses, tracing setup, and ServiceExt router tests.
- Axum Router handler modules
- Tower middleware configuration
- Structured error types implementing IntoResponse
By the numbers
- Skill version 1.1.1 with progressive disclosure at ~140 entry tokens and ~5500 full tokens
- Parent claude-mpm-skills repository bundles 171 skills including 6 Rust toolchain skills
Files
Axum (Rust) - Production Web APIs
Overview
Axum is a Rust web framework built on Hyper and Tower. Use it for type-safe request handling with composable middleware, structured errors, and excellent testability.
Quick Start
Minimal server
✅ Correct: typed handler + JSON response
use axum::{routing::get, Json, Router};
use serde::Serialize;
use std::net::SocketAddr;
#[derive(Serialize)]
struct Health {
status: &'static str,
}
async fn health() -> Json<Health> {
Json(Health { status: "ok" })
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/health", get(health));
let addr: SocketAddr = "0.0.0.0:3000".parse().unwrap();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}❌ Wrong: block the async runtime
async fn handler() {
std::thread::sleep(std::time::Duration::from_secs(1)); // blocks executor
}Core Concepts
Router + handlers
Handlers are async functions that return something implementing IntoResponse.
✅ Correct: route nesting
use axum::{routing::get, Router};
fn router() -> Router {
let api = Router::new()
.route("/users", get(list_users))
.route("/users/:id", get(get_user));
Router::new().nest("/api/v1", api)
}
async fn list_users() -> &'static str { "[]" }
async fn get_user() -> &'static str { "{}" }Extractors
Prefer extractors for parsing and validation at the boundary:
Path<T>: typed path paramsQuery<T>: query stringsJson<T>: JSON bodiesState<T>: shared application state
✅ Correct: typed path + JSON
use axum::{extract::Path, Json};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct CreateUser {
email: String,
}
#[derive(Serialize)]
struct User {
id: String,
email: String,
}
async fn create_user(Json(body): Json<CreateUser>) -> Json<User> {
Json(User { id: "1".into(), email: body.email })
}
async fn get_user(Path(id): Path<String>) -> Json<User> {
Json(User { id, email: "a@example.com".into() })
}Dependencies & Crate Structure
If your crate is published as a library in addition to being built as a binary, isolate the HTTP stack to avoid bloating library consumers.
✅ Correct: optional HTTP feature
[dependencies]
axum = { version = "0.7", optional = true }
tower-http = { version = "0.5", optional = true }
tokio = { version = "1", features = ["full"] }
[features]
default = ["http-server"]
http-server = ["axum", "tower-http"]
[[bin]]
name = "my-service"
required-features = ["http-server"]This way:
- Library consumers (
cargo add my-lib) get just the core logic without the HTTP overhead. - Binary builds include the server by default:
cargo install my-crateworks as expected. - Opt-out is explicit:
cargo add my-crate --no-default-featuresfor library use.
❌ Wrong: unconditional HTTP dependencies
# Never do this if the crate is also a library:
axum = "0.7"
tower-http = "0.5"
# Library users now pull in the entire web stackProduction Patterns
1) Shared state (DB pool, config, clients)
Use State<Arc<AppState>> and keep state immutable where possible.
✅ Correct: AppState via Arc
use axum::{extract::State, routing::get, Router};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
build_sha: &'static str,
}
async fn version(State(state): State<Arc<AppState>>) -> String {
state.build_sha.to_string()
}
fn app(state: Arc<AppState>) -> Router {
Router::new().route("/version", get(version)).with_state(state)
}2) Structured error handling (IntoResponse)
Centralize error mapping to HTTP status codes and JSON.
✅ Correct: AppError converts into response
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;
#[derive(Debug)]
enum AppError {
NotFound,
BadRequest(&'static str),
Internal,
}
#[derive(Serialize)]
struct ErrorBody {
error: &'static str,
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, msg) = match self {
AppError::NotFound => (StatusCode::NOT_FOUND, "not_found"),
AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"),
AppError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "internal"),
};
(status, Json(ErrorBody { error: msg })).into_response()
}
}3) Middleware (Tower layers)
Use tower-http for production-grade layers: tracing, timeouts, request IDs, CORS.
✅ Correct: trace + timeout + CORS
use axum::{routing::get, Router};
use std::time::Duration;
use tower::ServiceBuilder;
use tower_http::{
cors::{Any, CorsLayer},
timeout::TimeoutLayer,
trace::TraceLayer,
};
fn app() -> Router {
let layers = ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(TimeoutLayer::new(Duration::from_secs(10)))
.layer(CorsLayer::new().allow_origin(Any));
Router::new()
.route("/health", get(|| async { "ok" }))
.layer(layers)
}4) Graceful shutdown
Terminate on SIGINT/SIGTERM and let in-flight requests drain.
✅ Correct: with_graceful_shutdown
async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c().await.ok();
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.ok()
.and_then(|mut s| s.recv().await);
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {}
_ = terminate => {}
}
}
#[tokio::main]
async fn main() {
let app = app();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}Ops: Graceful Shutdown in Supervised Environments
Signal handling above is application-level. In production, your supervisor (systemd, launchd, container orchestrator) controls the actual termination window.
systemd (Linux): Set KillSignal=SIGTERM and TimeoutStopSec=120 (or higher) in your .service file.
- The default 90s timeout can fire mid-fsync on networked storage (EBS/EFS), truncating writes.
- 120s gives in-flight HTTP requests time to drain plus a safety margin for filesystem syncs.
launchd (macOS): Use launchctl bootout (sends SIGTERM and waits) instead of launchctl kickstart -k (SIGKILL, truncates in-flight I/O).
Client-side reconnection: Have HTTP clients and MCP bridges connecting to the service implement exponential backoff (starting 200ms, capped at 30s) so brief restarts are transparent and don't cascade errors upstream.
Testing
Test routers without sockets using tower::ServiceExt.
✅ Correct: request/response test
use axum::{body::Body, http::Request, Router};
use tower::ServiceExt;
#[tokio::test]
async fn health_returns_ok() {
let app: Router = super::app();
let res = app
.oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(res.status(), 200);
}Decision Trees
Axum vs other Rust frameworks
- Prefer Axum for Tower middleware composition and typed extractors.
- Prefer Actix Web for a mature ecosystem and actor-style runtime model.
- Prefer Warp for functional filters and minimalism.
Anti-Patterns
- Block the async runtime (
std::thread::sleep, blocking I/O inside handlers). - Use
unwrap()in request paths; return structured errors instead. - Run without timeouts; add request timeouts and upstream deadlines.
Resources
- Axum docs: https://docs.rs/axum
- Tower HTTP layers: https://docs.rs/tower-http
- Tracing: https://docs.rs/tracing
{
"name": "axum",
"version": "1.1.1",
"category": "toolchain",
"toolchain": "rust",
"tags": [
"rust",
"axum",
"tokio",
"http",
"api",
"tower",
"middleware",
"tracing",
"testing",
"graceful-shutdown",
"ops",
"feature-gates"
],
"entry_point_tokens": 140,
"full_tokens": 2100,
"related_skills": [
"docker",
"github-actions",
"systematic-debugging",
"verification-before-completion"
],
"author": "Claude MPM Team",
"license": "MIT",
"requires": [],
"repository": "https://github.com/bobmatnyc/claude-mpm-skills",
"created": "2025-12-17",
"updated": "2026-06-15"
}
Related skills
How it compares
Pick axum over generic API-design skills when the stack is Rust and you need Axum-specific extractor, Tower layer, and ServiceExt testing idioms.
FAQ
What Rust crates does the axum skill assume?
The axum skill assumes axum on Hyper and Tower with Tokio as the async runtime and commonly tower-http for tracing, timeouts, and CORS layers. Its quick start lists adding axum, tokio, and tower-http before defining Router handlers and AppState.
When should I pick Axum over Actix Web?
The axum skill recommends Axum when developers need Tower middleware composition and typed extractors at request boundaries. It points to Actix Web when prioritizing a mature ecosystem or actor-style runtime patterns instead of Tower layers.
Does the axum skill include testing patterns?
Yes. The axum skill documents testing routers with tower::ServiceExt so handlers can be exercised without binding a live TCP port. That pattern supports fast unit tests for status codes, JSON bodies, and middleware ordering.