pub struct Router { /* private fields */ }Expand description
HTTP router for managing routes, middleware, and request dispatching.
The Router is the central component for routing HTTP requests to appropriate
handlers. It supports dynamic path parameters, middleware chains, plugin integration,
and global state management. Routes are matched based on HTTP method and path pattern,
with support for trailing slash redirection and parameter extraction.
§Examples
use tako::{router::Router, Method, responder::Responder, types::Request};
async fn index(_req: Request) -> impl Responder {
"Welcome to the home page!"
}
async fn user_profile(_req: Request) -> impl Responder {
"User profile page"
}
let mut router = Router::new();
router.route(Method::GET, "/", index);
router.route(Method::GET, "/users/{id}", user_profile);
router.state("app_name", "MyApp".to_string());Implementations§
Source§impl Router
impl Router
Sourcepub fn route<H, T>(
&mut self,
method: Method,
path: &str,
handler: H,
) -> Arc<Route> ⓘ
pub fn route<H, T>( &mut self, method: Method, path: &str, handler: H, ) -> Arc<Route> ⓘ
Registers a new route with the router.
Associates an HTTP method and path pattern with a handler function. The path
can contain dynamic segments using curly braces (e.g., /users/{id}), which
are extracted as parameters during request processing.
§Panics
Panics if a route with the same method and path pattern is already registered.
§Examples
use tako::{router::Router, Method, responder::Responder, types::Request};
async fn get_user(_req: Request) -> impl Responder {
"User details"
}
async fn create_user(_req: Request) -> impl Responder {
"User created"
}
let mut router = Router::new();
router.route(Method::GET, "/users/{id}", get_user);
router.route(Method::POST, "/users", create_user);
router.route(Method::GET, "/health", |_req| async { "OK" });Sourcepub fn get<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn get<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a GET route. Shorthand for Router::route with Method::GET.
Sourcepub fn post<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn post<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a POST route. Shorthand for Router::route with Method::POST.
Sourcepub fn put<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn put<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a PUT route. Shorthand for Router::route with Method::PUT.
Sourcepub fn delete<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn delete<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a DELETE route. Shorthand for Router::route with Method::DELETE.
Sourcepub fn patch<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn patch<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a PATCH route. Shorthand for Router::route with Method::PATCH.
Sourcepub fn head<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn head<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers a HEAD route. Shorthand for Router::route with Method::HEAD.
Sourcepub fn options<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
pub fn options<H, T>(&mut self, path: &str, handler: H) -> Arc<Route> ⓘ
Registers an OPTIONS route. Shorthand for Router::route with Method::OPTIONS.
Sourcepub fn mount_all(&mut self) -> &mut Router
pub fn mount_all(&mut self) -> &mut Router
Registers every route declared via the #[tako::route] / #[tako::get]
(and friends) attribute macros into this router.
Each macro contributes a thunk into the global TAKO_ROUTES slice at
link time; this method walks the slice and invokes each thunk against
self, which calls Router::route under the hood. Routes are
registered in the order the linker emits them — typically the order they
appear within a translation unit, but unspecified across crates. If two
thunks register the same (method, path) pair, the second call will
panic, matching the behavior of Router::route.
§Why linkme and not explicit registration
We keep the linkme distributed-slice strategy on purpose. The
alternative — an explicit register_routes!(my_crate::routes) invocation
per crate — was considered and rejected because:
- Adding a handler would require touching three places (the handler itself, the per-crate registration list, and the call site that imports it) instead of one. The macro authoring story is the main reason teams pick attribute routing in the first place.
- Cross-crate path collisions panic at startup either way; explicit registration does not buy any extra safety.
- Link-order non-determinism only matters when two routes share a
(method, path)pair — that is already a hard failure and a CI test catches it deterministically. - Prefix grouping is already covered by
Router::mount_all_into, so “I want all my routes under/api” does not require explicit registration.
Callers that need stable, deterministic ordering should call
Router::route directly.
§Examples
use tako::{get, router::Router};
#[get("/health")]
async fn health() -> impl tako::responder::Responder { "ok" }
let mut router = Router::new();
router.mount_all();Sourcepub fn mount_all_into(&mut self, prefix: &str) -> &mut Router
pub fn mount_all_into(&mut self, prefix: &str) -> &mut Router
Like Router::mount_all but registers every macro-declared route under
the given path prefix. The prefix is normalized (trailing / stripped),
then prepended to each registered path. Useful when you want, e.g., all
#[get("/users")] declarations to live under /api.
Ordering across crates remains the linker’s choice (see
Router::mount_all for details).
§Examples
let mut router = Router::new();
router.mount_all_into("/api"); // /users → /api/users, /health → /api/healthSourcepub fn scope<F>(&mut self, prefix: &str, build: F) -> &mut Router
pub fn scope<F>(&mut self, prefix: &str, build: F) -> &mut Router
Registers a group of routes under a shared path prefix.
The closure receives self with the prefix active, so any route() /
get() / post() etc. calls inside register the routes with the prefix
prepended. Prefixes nest: a scope("/v1", |r| r.scope("/users", …))
produces routes under /v1/users. Cold path; no dispatch impact.
§Examples
use tako::router::Router;
use tako::responder::Responder;
async fn list_users() -> impl Responder { "users" }
async fn create_user() -> impl Responder { "created" }
let mut router = Router::new();
router.scope("/api/v1", |r| {
r.get("/users", list_users);
r.post("/users", create_user);
});Sourcepub fn nest(&mut self, prefix: &str, child: Router) -> &mut Router
pub fn nest(&mut self, prefix: &str, child: Router) -> &mut Router
Mounts every route from a child router under the given path prefix.
Unlike Router::merge, nest builds new Arc<Route> instances for
each child route via Route::cloned_with_path — so re-nesting the same
child cannot double-stack its global middleware onto the same shared
Arc<Route>. The child router’s global middleware chain is prepended to
each newly-registered route’s middleware chain (so child globals run
before child-route middleware at dispatch time).
Caveats:
- Route-level plugins on the child are not carried over.
- The child’s fallback / error handlers are not inherited.
§Panics
Panics at registration time if mounting the child would conflict with a
route already present on self (same method + same prefixed path).
Mirrors the behavior of Router::route — route registration is a
startup-time operation and conflicts are configuration bugs, not
runtime conditions.
§Examples
use tako::router::Router;
use tako::responder::Responder;
async fn list_users() -> impl Responder { "users" }
let mut api = Router::new();
api.get("/users", list_users);
let mut root = Router::new();
root.nest("/api/v1", api); // /users → /api/v1/usersSourcepub fn route_with_tsr<H, T>(
&mut self,
method: Method,
path: &str,
handler: H,
) -> Arc<Route> ⓘ
pub fn route_with_tsr<H, T>( &mut self, method: Method, path: &str, handler: H, ) -> Arc<Route> ⓘ
Registers a route with trailing slash redirection enabled.
When TSR is enabled, requests to paths with or without trailing slashes are automatically redirected to the canonical version. This helps maintain consistent URLs and prevents duplicate content issues.
§Panics
- Panics if called with the root path (
"/") since TSR is not applicable. - Panics if a route with the same method and path pattern is already registered.
§Examples
use tako::{router::Router, Method, responder::Responder, types::Request};
async fn api_handler(_req: Request) -> impl Responder {
"API endpoint"
}
let mut router = Router::new();
// Both "/api" and "/api/" will redirect to the canonical form
router.route_with_tsr(Method::GET, "/api", api_handler);Sourcepub async fn dispatch(&self, req: Request<TakoBody>) -> Response<TakoBody>
pub async fn dispatch(&self, req: Request<TakoBody>) -> Response<TakoBody>
Dispatches an incoming request to the appropriate route handler.
Sourcepub fn state<T>(&mut self, value: T)
pub fn state<T>(&mut self, value: T)
Adds a value to the global type-based state accessible by all handlers.
Global state allows sharing data across different routes and middleware.
Values are stored by their concrete type and retrieved via the
State extractor (from tako-extractors) or with
crate::state::get_state.
§Examples
use tako::router::Router;
#[derive(Clone)]
struct AppConfig { database_url: String, api_key: String }
let mut router = Router::new();
router.state(AppConfig {
database_url: "postgresql://localhost/mydb".to_string(),
api_key: "secret-key".to_string(),
});
// You can also store simple types by type:
router.state::<String>("1.0.0".to_string());Sourcepub fn with_state<T>(&mut self, value: T) -> &mut Router
pub fn with_state<T>(&mut self, value: T) -> &mut Router
Inserts a value into this router’s instance-local typed state.
Unlike Router::state (which writes the process-global registry and
therefore allows only one value per T per process), with_state is
per-router — multiple routers can hold distinct Ts without collisions.
The State extractor (from tako-extractors) reads the per-router
store first and falls back to the global store if no per-router value
exists, so existing code that uses set_state / Router::state
continues to work unchanged.
Hot-path cost is one Arc clone per request only when at least one
with_state call has happened on this router; an AtomicBool::Acquire
fast-path skips it for routers that don’t use instance-local state.
§Examples
use tako::router::Router;
#[derive(Clone)]
struct Db;
let mut router = Router::new();
router.with_state(Db);Sourcepub fn router_state(&self) -> &Arc<RouterState> ⓘ
pub fn router_state(&self) -> &Arc<RouterState> ⓘ
Returns the per-router typed state (shared Arc).
Sourcepub fn signals(&self) -> &SignalArbiter
Available on crate feature signals only.
pub fn signals(&self) -> &SignalArbiter
signals only.Returns a reference to the signal arbiter.
Sourcepub fn signal_arbiter(&self) -> SignalArbiter
Available on crate feature signals only.
pub fn signal_arbiter(&self) -> SignalArbiter
signals only.Returns a clone of the signal arbiter, useful for sharing through state.
Sourcepub fn on_signal<F, Fut>(&self, id: impl Into<String>, handler: F)
Available on crate feature signals only.
pub fn on_signal<F, Fut>(&self, id: impl Into<String>, handler: F)
signals only.Registers a handler for a named signal on this router’s arbiter.
Sourcepub async fn emit_signal(&self, signal: Signal)
Available on crate feature signals only.
pub async fn emit_signal(&self, signal: Signal)
signals only.Emits a signal through this router’s arbiter.
Sourcepub fn middleware<F, Fut, R>(&self, f: F) -> &Router
pub fn middleware<F, Fut, R>(&self, f: F) -> &Router
Adds global middleware to the router.
Global middleware is executed for all routes in the order it was added, before any route-specific middleware. Middleware can modify requests, generate responses, or perform side effects like logging or authentication.
§Examples
use tako::{router::Router, middleware::Next, types::Request};
let mut router = Router::new();
// Logging middleware
router.middleware(|req, next| async move {
println!("Request: {} {}", req.method(), req.uri());
let response = next.run(req).await;
println!("Response: {}", response.status());
response
});
// Authentication middleware
router.middleware(|req, next| async move {
if req.headers().contains_key("authorization") {
next.run(req).await
} else {
"Unauthorized".into_response()
}
});Sourcepub fn fallback<F, Fut, R>(&mut self, handler: F) -> &mut Router
pub fn fallback<F, Fut, R>(&mut self, handler: F) -> &mut Router
Sets a fallback handler that will be executed when no route matches.
The fallback runs after global middlewares and can be used to implement custom 404 pages, catch-all logic, or method-independent handlers.
§Examples
use tako::{router::Router, Method, responder::Responder, types::Request};
async fn not_found(_req: Request) -> impl Responder { "Not Found" }
let mut router = Router::new();
router.route(Method::GET, "/", |_req| async { "Hello" });
router.fallback(not_found);Sourcepub fn fallback_with_extractors<H, T>(&mut self, handler: H) -> &mut Router
pub fn fallback_with_extractors<H, T>(&mut self, handler: H) -> &mut Router
Sets a fallback handler that supports extractors (like Path, Query, etc.).
Use this when your fallback needs to parse request data via extractors. If you
only need access to the raw Request, prefer fallback for simpler type inference.
§Examples
use tako::{router::Router, responder::Responder, extractors::{path::Path, query::Query}};
#[derive(serde::Deserialize)]
struct Q { q: Option<String> }
async fn fallback_with_q(Path(_p): Path<String>, Query(_q): Query<Q>) -> impl Responder {
"Not Found"
}
let mut router = Router::new();
router.fallback_with_extractors(fallback_with_q);Sourcepub fn timeout(&mut self, duration: Duration) -> &mut Router
pub fn timeout(&mut self, duration: Duration) -> &mut Router
Sets a default timeout for all routes.
This timeout can be overridden on individual routes using Route::timeout.
When a request exceeds the timeout duration, the timeout fallback handler
is invoked (if configured) or a 408 Request Timeout response is returned.
§Examples
use tako::router::Router;
use std::time::Duration;
let mut router = Router::new();
router.timeout(Duration::from_secs(30));Sourcepub fn timeout_fallback<F, Fut, R>(&mut self, handler: F) -> &mut Router
pub fn timeout_fallback<F, Fut, R>(&mut self, handler: F) -> &mut Router
Sets a fallback handler that will be executed when a request times out.
If no timeout fallback is set, a default 408 Request Timeout response is returned.
§Examples
use tako::{router::Router, responder::Responder, types::Request};
use std::time::Duration;
async fn timeout_handler(_req: Request) -> impl Responder {
"Request took too long"
}
let mut router = Router::new();
router.timeout(Duration::from_secs(30));
router.timeout_fallback(timeout_handler);Sourcepub fn error_handler(
&mut self,
handler: impl Fn(Response<TakoBody>) -> Response<TakoBody> + Send + Sync + 'static,
) -> &mut Router
pub fn error_handler( &mut self, handler: impl Fn(Response<TakoBody>) -> Response<TakoBody> + Send + Sync + 'static, ) -> &mut Router
Sets a global error handler for 5xx responses.
The error handler receives any response with a server error status and can transform it (e.g., to return JSON-formatted errors instead of plain text).
§Examples
use tako::router::Router;
use tako::body::TakoBody;
let mut router = Router::new();
router.error_handler(|resp| {
let status = resp.status();
let body = format!(r#"{{"error": "{}"}}"#, status.canonical_reason().unwrap_or("Unknown"));
let mut res = http::Response::new(TakoBody::from(body));
*res.status_mut() = status;
res.headers_mut().insert(
http::header::CONTENT_TYPE,
http::HeaderValue::from_static("application/json"),
);
res
});Sourcepub fn client_error_handler(
&mut self,
handler: impl Fn(Response<TakoBody>) -> Response<TakoBody> + Send + Sync + 'static,
) -> &mut Router
pub fn client_error_handler( &mut self, handler: impl Fn(Response<TakoBody>) -> Response<TakoBody> + Send + Sync + 'static, ) -> &mut Router
Sets a global error handler for 4xx responses.
Mirrors Router::error_handler but fires for client errors. Useful for
converting bare 404 / 405 / 422 responses into structured error documents
(e.g. via crate::problem::default_problem_responder).
Sourcepub fn use_problem_json(&mut self) -> &mut Router
pub fn use_problem_json(&mut self) -> &mut Router
Convenience: install crate::problem::default_problem_responder for
both 4xx and 5xx so unhandled errors always render as
application/problem+json.
Sourcepub fn plugin<P>(&mut self, plugin: P) -> &mut Router
Available on crate feature plugins only.
pub fn plugin<P>(&mut self, plugin: P) -> &mut Router
plugins only.Registers a plugin with the router.
Plugins extend the router’s functionality by providing additional features like compression, CORS handling, rate limiting, or custom behavior. Plugins are initialized once when the server starts.
§Examples
use tako::{router::Router, plugins::TakoPlugin};
use anyhow::Result;
struct LoggingPlugin;
impl TakoPlugin for LoggingPlugin {
fn name(&self) -> &'static str {
"logging"
}
fn setup(&self, _router: &Router) -> Result<()> {
println!("Logging plugin initialized");
Ok(())
}
}
let mut router = Router::new();
router.plugin(LoggingPlugin);Sourcepub fn merge(&mut self, other: Router)
pub fn merge(&mut self, other: Router)
Merges another router into this router.
This method combines routes and middleware from another router into the current one. Routes are copied over, and the other router’s global middleware is prepended to each merged route’s middleware chain.
§Panics
Panics at registration time if a merged route conflicts with one already
present on self (same method + same path). Mirrors the behavior of
Router::route and Router::nest — merge is a startup-time
operation and route conflicts are configuration bugs.
§Examples
use tako::{router::Router, Method, responder::Responder, types::Request};
async fn api_handler(_req: Request) -> impl Responder {
"API response"
}
async fn web_handler(_req: Request) -> impl Responder {
"Web response"
}
// Create API router
let mut api_router = Router::new();
api_router.route(Method::GET, "/users", api_handler);
api_router.middleware(|req, next| async move {
println!("API middleware");
next.run(req).await
});
// Create main router and merge API router
let mut main_router = Router::new();
main_router.route(Method::GET, "/", web_handler);
main_router.merge(api_router);Sourcepub fn collect_openapi_routes(&self) -> Vec<(Method, String, RouteOpenApi)>
Available on crate features utoipa or vespera only.
pub fn collect_openapi_routes(&self) -> Vec<(Method, String, RouteOpenApi)>
utoipa or vespera only.Collects OpenAPI metadata from all registered routes.
Returns a vector of tuples containing the HTTP method, path, and OpenAPI
metadata for each route that has OpenAPI information attached.
§Examples
use tako::{router::Router, Method};
let mut router = Router::new();
router.route(Method::GET, "/users", list_users)
.summary("List users")
.tag("users");
for (method, path, openapi) in router.collect_openapi_routes() {
println!("{} {} - {:?}", method, path, openapi.summary);
}Sourcepub fn compact_routes(&mut self)
pub fn compact_routes(&mut self)
Drops dangling Weak<Route> entries from the per-method routes index.
All current routes stay live for the router’s lifetime, so this is a
no-op in well-behaved code. It exists as a safety valve: if any future
API ever removes from inner (hot reload, route deregistration), or if
downstream code holds the Arc<Route> returned from Router::route
past the router’s lifetime, this method bounds the size of the index.
Cold path; safe to call repeatedly. Linear in the total number of registered routes.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Router
impl !RefUnwindSafe for Router
impl Send for Router
impl Sync for Router
impl Unpin for Router
impl UnsafeUnpin for Router
impl !UnwindSafe for Router
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
Source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more