MatchedPath is an extractor that provides the route pattern (not the actual request path) that matched the current request.
Basic usage
use axum::{Router, routing::get, extract::MatchedPath};
let app = Router::new().route(
"/users/{id}",
get(|path: MatchedPath| async move {
let pattern = path.as_str();
// pattern will be "/users/{id}", not "/users/123"
format!("Matched pattern: {}", pattern)
})
);
The matched path is the route pattern (e.g., /users/{id}), not the actual request URI (e.g., /users/123). Use Uri or OriginalUri to get the actual request path.
Using in middleware
MatchedPath is commonly used in middleware for logging, metrics, and tracing:
use axum::{
Router,
routing::get,
extract::{MatchedPath, Request},
};
use tower_http::trace::TraceLayer;
let app = Router::new()
.route("/users/{id}", get(|| async { /* ... */ }))
.layer(
TraceLayer::new_for_http().make_span_with(|req: &Request<_>| {
let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
matched_path.as_str()
} else {
req.uri().path()
};
tracing::info_span!("http-request", %path)
})
);
Nested routes
When using nested routers, MatchedPath includes the full pattern from all nesting levels:
use axum::{Router, routing::get, extract::MatchedPath};
let api_routes = Router::new().route(
"/{version}",
get(|path: MatchedPath| async move {
// path.as_str() will be "/api/{version}"
path.as_str().to_owned()
})
);
let app = Router::new().nest("/api", api_routes);
MatchedPath won’t be available in fallback handlers or when using nest_service. Use Option<MatchedPath> to handle these cases:
use axum::{Router, routing::get, extract::MatchedPath};
async fn handler(path: Option<MatchedPath>) -> String {
match path {
Some(p) => format!("Matched: {}", p.as_str()),
None => "No matched path available".to_string(),
}
}
let app = Router::new().fallback(handler);
MatchedPath is not available in fallback handlers. It will always be None when extracted in a fallback.
Type signature
Returns a string representation of the matched route pattern.
Rejection
Rejects with MatchedPathMissing if:
- The request didn’t match any route (fallback handler)
- Used with
nest_service instead of nest
- Extracted before routing has occurred
Common use cases
- Structured logging: Group logs by route pattern instead of specific paths
- Metrics collection: Track request counts and latencies per route
- Rate limiting: Apply different rate limits based on route patterns
- Debugging: Understand which route handler is processing a request
Example: Metrics middleware
use axum::{
Router,
routing::get,
extract::Request,
middleware::{self, Next},
response::Response,
};
use std::time::Instant;
async fn track_metrics(req: Request, next: Next) -> Response {
let path = req.extensions()
.get::<axum::extract::MatchedPath>()
.map(|p| p.as_str().to_owned())
.unwrap_or_else(|| "unknown".to_string());
let start = Instant::now();
let response = next.run(req).await;
let duration = start.elapsed();
// Record metrics with route pattern, not specific path
println!("Route: {}, Duration: {:?}", path, duration);
response
}
let app = Router::new()
.route("/users/{id}", get(|| async { "User" }))
.layer(middleware::from_fn(track_metrics));
See also
- Uri - Extract the actual request URI
- OriginalUri - Extract the original URI before nesting modifications