Why tower?
While it’s not necessary to fully understand tower to write or use middleware with axum, having at least a basic understanding of tower’s concepts is recommended. Tower provides a standardized interface for composable middleware through theService and Layer traits.
Resources:
- tower’s guides - General introduction to tower
- tower::ServiceBuilder - Composing multiple middleware
Applying middleware
Axum allows you to add middleware at multiple levels:- Entire routers:
Router::layerandRouter::route_layer - Method routers:
MethodRouter::layerandMethodRouter::route_layer - Individual handlers:
Handler::layer
Basic layer application
Applying multiple middleware
Usetower::ServiceBuilder to apply multiple middleware efficiently:
Commonly used tower-http middleware
The tower-http crate provides many production-ready middleware:TraceLayer
High-level tracing and logging:CorsLayer
Cross-Origin Resource Sharing (CORS) handling:CompressionLayer
Automatic compression of responses:TimeoutLayer
Timeout requests after a duration:RequestIdLayer
Set and propagate request IDs:Middleware ordering
Router::layer ordering
When you add middleware withRouter::layer, all previously added routes are wrapped. Middleware executes from bottom to top:
layer_threereceives the request- Passes to
layer_two - Passes to
layer_one - Passes to
handler(produces response) - Response goes back through
layer_one - Then
layer_two - Finally
layer_three
ServiceBuilder ordering
ServiceBuilder composes layers to execute top to bottom, which is more intuitive:
layer_onereceives request first- Then
layer_two - Then
layer_three - Then
handler - Response bubbles back up through
layer_three,layer_two,layer_one
ServiceBuilder for better readability and mental model.
The layer method
Thelayer method is available on Router, MethodRouter, and Handler. It accepts any type implementing tower::Layer:
Applying to routers
Applying to specific routes
Applying to handlers
Writing custom tower middleware
For maximum control, implementtower::Service and tower::Layer:
- Middleware needs to be configurable via builder methods
- You intend to publish middleware for others to use
- You need maximum performance (avoid boxing futures)
Tower combinators
Tower provides utility combinators for simple transformations:map_request
map_response
then
Chain an async function after the service:and_then
Chain a fallible async function:Wrapping the entire app
Apply middleware around your entire application by wrapping theRouter (which implements Service):
- Middleware that needs to run before routing
- URI rewriting middleware
- Backpressure-sensitive middleware
Rewriting request URIs
Middleware added withRouter::layer runs after routing. To rewrite URIs before routing, wrap the entire Router:
Error handling
Axum’s error handling model requires handlers to always return a response. Middleware can introduce errors, so they must be handled gracefully usingHandleErrorLayer:
HandleErrorLayer must be placed above middleware that can produce errors.
Accessing state in tower layers
Pass state to custom tower layers through the layer’s constructor:Backpressure considerations
Axum expects services to not care about backpressure and always be ready. When routing to multiple services, axum:- Always returns
Poll::Ready(Ok(()))fromService::poll_ready - Drives actual readiness inside the response future
- Avoid routing to backpressure-sensitive middleware
- Use load shedding if needed
- Apply backpressure-sensitive middleware around the entire app
- Errors from
poll_readyappear in the response future, not immediately
See also
- middleware::from_fn - Create middleware from async functions
- error handling - Axum’s error handling model
- tower documentation - Tower service and layer traits
- tower-http documentation - HTTP-specific middleware