rust
60 lines · 8 steps
Passing per-request context through Axum middleware
An Axum middleware layer builds a per-request context and hands it to a handler via request extensions.
Explained by
highlit
1use std::sync::Arc;
2use axum::{
3 body::Body,
4 extract::{Extension, State},
5 http::{Request, StatusCode},
6 middleware::Next,
7 response::{IntoResponse, Response},
8 Json,
9};
10use serde_json::json;
11use uuid::Uuid;
12
13#[derive(Clone)]
14struct AppState {
15 db: Arc<PgPool>,
16 feature_flags: Arc<FeatureFlags>,
17}
18
19#[derive(Clone)]
20struct RequestContext {
21 request_id: Uuid,
22 tenant: String,
23}
24
25async fn attach_context(mut req: Request<Body>, next: Next) -> Result<Response, StatusCode> {
26 let tenant = req
27 .headers()
28 .get("x-tenant-id")
29 .and_then(|v| v.to_str().ok())
30 .ok_or(StatusCode::BAD_REQUEST)?
31 .to_owned();
32
33 let ctx = RequestContext {
34 request_id: Uuid::new_v4(),
35 tenant,
36 };
37
38 req.extensions_mut().insert(ctx);
39 Ok(next.run(req).await)
40}
41
42async fn current_usage(
43 State(state): State<AppState>,
44 Extension(ctx): Extension<RequestContext>,
45) -> impl IntoResponse {
46 let quota = sqlx::query_scalar!(
47 "SELECT quota FROM tenants WHERE id = $1",
48 ctx.tenant
49 )
50 .fetch_one(&*state.db)
51 .await
52 .unwrap_or(0);
53
54 Json(json!({
55 "request_id": ctx.request_id,
56 "tenant": ctx.tenant,
57 "quota": quota,
58 "metering_enabled": state.feature_flags.is_enabled("metering"),
59 }))
60}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Request extensions let middleware attach typed data that downstream handlers pull out with the Extension extractor.
- 2Separating shared app state from per-request context keeps long-lived resources distinct from data that changes each call.
- 3Failing fast in middleware with a StatusCode error rejects malformed requests before any handler runs.
Related explainers
ruby
module RequestTagging class Middleware def initialize(app) @app = app
Per-request context with CurrentAttributes in Rails
middleware
thread-safety
logging
Intermediate
7 steps
typescript
type Middleware<TIn, TOut> = (ctx: TIn) => Promise<TOut> | TOut; class Pipeline<TIn, TOut> { private constructor(private readonly run: Middleware<TIn, TOut>) {}
A type-safe async middleware pipeline
generics
type-safety
middleware
Advanced
9 steps
rust
use std::time::{Duration, Instant}; pub struct Ewma { alpha: f64,
A time-decayed moving average in Rust
exponential-smoothing
time-decay
state-machine
Intermediate
8 steps
java
@Component public class HeaderMergeFilter { private static final String PER_REQUEST_HEADERS = HeaderMergeFilter.class.getName() + ".headers";
Merging default and per-request headers in Spring
webclient
filters
http-headers
Intermediate
8 steps
go
package middleware import ( "net/http"
Wrapping Gin requests in a DB transaction
middleware
transactions
error-handling
Intermediate
8 steps
php
<?php namespace App\Http\Middleware;
Caching HTTP responses with Laravel middleware
middleware
caching
http
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/passing-per-request-context-through-axum-middleware-explained-rust-192a/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.