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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Middleware can inspect the request before and mutate the response after the inner handler runs.
- 2Storing configuration in shared state lets every request read the same version policy without globals.
- 3Deprecation is communicated to clients through standard headers like deprecation, sunset, and link.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
rust
use std::cmp::{Ordering, Reverse}; use std::collections::BinaryHeap; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Lines, Write};
K-way merge of sorted logs in Rust
binary-heap
k-way-merge
streaming-io
Intermediate
8 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
rust
use std::f64::consts::PI; #[derive(Debug, Clone, Copy)] pub struct LatLng {
Building geographic bounding boxes in Rust
geospatial
value-types
option
Intermediate
7 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 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/api-version-headers-with-axum-middleware-explained-rust-ef46/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.