Middleware
Neutron middleware wraps request/response processing. Middleware is composable, scoped to routers, and zero-allocation at startup.
Using Middleware
let router = Router::new()
.middleware(Logger)
.middleware(RequestId::new())
.middleware(Timeout::from_secs(30))
.get("/", handler);
Order matters — middleware executes top-to-bottom on request, bottom-to-top on response.
Writing Custom Middleware
Any async function matching (Request, Next) -> Response is middleware:
async fn timing(req: Request, next: Next) -> Response {
let start = std::time::Instant::now();
let mut resp = next.run(req).await;
resp.headers_mut().insert(
"x-response-time",
format!("{}ms", start.elapsed().as_millis()).parse().unwrap(),
);
resp
}
let router = Router::new()
.middleware(timing)
.get("/", handler);
Scoped Middleware
Middleware applied to a nested router only affects its routes:
let public = Router::new()
.get("/health", health);
let api = Router::new()
.middleware(JwtAuth::new(config)) // Only applies to /api/*
.get("/items", list_items);
let router = Router::new()
.middleware(Logger) // Applies to all routes
.nest("/api", api)
.merge(public);
Recommended Stack
The 10-layer middleware stack in recommended order:
let router = Router::new()
// 1. Panic recovery → 500
.middleware(CatchPanic::new())
// 2. Request logging
.middleware(Logger)
// 3. Unique request IDs
.middleware(RequestId::new())
// 4. Request timeout
.middleware(Timeout::from_secs(30))
// 5. CORS headers
.middleware(CORS::new().allow_any_origin())
// 6. Security headers
.middleware(Helmet::new())
// 7. Rate limiting
.middleware(RateLimiter::new(100, Duration::from_secs(60)))
// 8. Body size limit
.middleware(BodyLimit::new(2 * 1024 * 1024)) // 2MB
// 9. Response compression
.middleware(Compress::new())
// 10. Your routes
.get("/", handler);
Built-In Middleware
Logging & Observability
| Middleware | Feature | Description |
|-----------|---------|-------------|
| Logger | logging | Request method, path, status, duration |
| RequestId::new() | request-id | X-Request-ID header generation |
| TracingLayer::new() | tracing-mw | OpenTelemetry distributed tracing |
| Metrics::new() | metrics | Prometheus-compatible metrics |
Security
| Middleware | Feature | Description |
|-----------|---------|-------------|
| Helmet::new() | helmet | HSTS, CSP, X-Frame-Options, etc. |
| CORS::new() | cors | Cross-origin resource sharing |
| CsrfLayer::new(key) | cookie | CSRF token validation |
| JwtAuth::new(config) | jwt | JWT Bearer token validation |
| SessionLayer::new(store, key) | cookie | Server-side sessions |
Performance
| Middleware | Feature | Description |
|-----------|---------|-------------|
| Compress::new() | compress | Gzip/Brotli response compression |
| ResponseCache::new(100) | cache | In-memory response caching with TTL |
| Deduplicate::new() | dedup | Coalesce duplicate in-flight requests |
| RateLimiter::new(n, dur) | rate-limit | Token bucket rate limiting |
| CircuitBreaker::new() | circuit-breaker | Circuit breaker pattern |
Safety
| Middleware | Feature | Description |
|-----------|---------|-------------|
| CatchPanic::new() | catch-panic | Convert panics to 500 responses |
| Timeout::from_secs(n) | timeout | Per-request timeout |
| BodyLimit::new(bytes) | body-limit | Request body size limit |
| HealthCheck::new() | health | GET /health endpoint |
Middleware Trait
For stateful middleware, implement the trait directly:
pub trait MiddlewareTrait: Send + Sync {
fn call(&self, req: Request, next: Next)
-> Pin<Box<dyn Future<Output = Response> + Send>>;
}