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
❌ Wrong: block the async runtime
Core Concepts
Router + handlers
Handlers are async functions that return something implementing IntoResponse.
✅ Correct: route nesting
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
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
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
Production Patterns
1) Shared state (DB pool, config, clients)
Use State<Arc<AppState>> and keep state immutable where possible.
✅ Correct: AppState via Arc
2) Structured error handling (IntoResponse)
Centralize error mapping to HTTP status codes and JSON.
✅ Correct: AppError converts into response
3) Middleware (Tower layers)
Use tower-http for production-grade layers: tracing, timeouts, request IDs, CORS.
✅ Correct: trace + timeout + CORS
4) Graceful shutdown
Terminate on SIGINT/SIGTERM and let in-flight requests drain.
✅ Correct: with_graceful_shutdown
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
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


