rust 53 lines · 8 steps

API version headers with Axum middleware

An Axum middleware stamps every response with the current API version and flags deprecated ones with sunset and link headers.

Explained by highlit
1use axum::{
2 extract::{Request, State},
3 http::{HeaderValue, StatusCode},
4 middleware::Next,
5 response::Response,
6};
7 
8#[derive(Clone)]
9pub struct ApiVersion {
10 pub current: &'static str,
11 pub deprecated: &'static [&'static str],
12}
13 
14pub async fn attach_api_version(
15 State(version): State<ApiVersion>,
16 request: Request,
17 next: Next,
18) -> Result<Response, StatusCode> {
19 let requested = request
20 .headers()
21 .get("accept-version")
22 .and_then(|v| v.to_str().ok())
23 .map(str::to_owned);
24 
25 let mut response = next.run(request).await;
26 
27 let headers = response.headers_mut();
28 headers.insert(
29 "api-version",
30 HeaderValue::from_static(version.current),
31 );
32 
33 if let Some(requested) = requested {
34 if version.deprecated.contains(&requested.as_str()) {
35 headers.insert(
36 "deprecation",
37 HeaderValue::from_static("true"),
38 );
39 headers.insert(
40 "sunset",
41 HeaderValue::from_static("Wed, 31 Dec 2025 23:59:59 GMT"),
42 );
43 if let Ok(link) = HeaderValue::from_str(&format!(
44 "</docs/migrate/{}>; rel=\"successor-version\"",
45 version.current
46 )) {
47 headers.insert("link", link);
48 }
49 }
50 }
51 
52 Ok(response)
53}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Middleware can inspect the request before and mutate the response after the inner handler runs.
  2. 2Storing configuration in shared state lets every request read the same version policy without globals.
  3. 3Deprecation is communicated to clients through standard headers like deprecation, sunset, and link.

Related explainers

Share this explainer

Here's the card — post it anywhere.

API version headers with Axum middleware — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code