Skip to main content
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);

Optional extraction

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

as_str()
&str
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