# Routing

> Map (method, path) pairs to handlers, run middleware, nest sub-routers, and emit method-aware responses with Tako's Router.

`Router` is the core dispatch type. It maps `(method, path)` pairs to
handlers, runs middleware, and answers `405 Method Not Allowed` (with a
populated `Allow` header) when the path matches but the method does
not. Path syntax is `matchit`-compatible: `{name}` for a free segment,
`{*rest}` for a catch-all, and `{name: T}` only inside the
`#[tako::route]` macro family for compile-time-typed slots.

## Building a router

```rust
use tako::Method;
use tako::router::Router;

let mut router = Router::new();

// Register routes with the explicit method form, …
router.route(Method::GET, "/", root);
router.route(Method::POST, "/users", create_user);

// … or the shorthand methods.
router.get("/health", health);
router.put("/users/{id}", update_user);
router.delete("/users/{id}", delete_user);

async fn root() -> &'static str { "ok" }
async fn health() -> &'static str { "ok" }
async fn create_user() -> &'static str { "created" }
async fn update_user() -> &'static str { "updated" }
async fn delete_user() -> &'static str { "deleted" }
```

`Router::route` returns `Arc<Route>`. Attach per-route middleware by
chaining `.middleware(mw)` on the returned route (the
[`Router::middleware`](https://docs.rs/tako-rs/latest/tako/router/struct.Router.html)
on the router itself adds *global* middleware that runs on every
request).

## Dynamic path parameters

```rust
use serde::Deserialize;
use tako::Method;
use tako::extractors::path::Path;
use tako::responder::Responder;
use tako::router::Router;

#[derive(Deserialize)]
struct UserPath { id: u64 }

async fn show_user(Path(p): Path<UserPath>) -> impl Responder {
  format!("user_id={}", p.id)
}

let mut router = Router::new();
router.route(Method::GET, "/users/{id}", show_user);
```

For catch-all suffixes use `{*rest}`:

```rust
router.get("/static/{*rest}", serve_static);
```

See [Extractors](/docs/extractors) for the full catalog of types you can
bind to handler arguments, including [`Path<T>`](/docs/extractors/request-meta).

### Typed slots via the route macro

The `#[tako::get]` / `#[tako::post]` / `#[tako::route]` macros parse
`{name: T}` at registration time and reject paths whose parameters
don't deserialise:

```rust
use tako::get;
use tako::extractors::typed_params::TypedParams;
use tako::responder::Responder;
use tako::router::Router;

#[get("/users/{id: u64}/posts/{post_id: u64}")]
async fn show_post(TypedParams(p): TypedParams<ShowPost>) -> impl Responder {
  format!("user={} post={}", p.user_id, p.post_id)
}

#[derive(serde::Deserialize)]
struct ShowPost { id: u64, post_id: u64 }

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  let mut router = Router::new();
  router.mount_all(); // picks up every macro-registered route

  let listener = tako::bind_with_port_fallback("127.0.0.1:3000").await?;
  tako::Server::builder().build().spawn_http(listener, router)
    .result().await.expect("HTTP server failed");
  Ok(())
}
```

`mount_all()` collects every route registered via the macro through
the `linkme` distributed-slice. To mount them under a prefix use
`mount_all_into("/api/v1")`. See
[`examples/typed-routes`](https://github.com/rust-dd/tako/tree/main/examples/typed-routes).

## Sub-routing: `nest` and `scope`

Both attach a prefix to a group of routes. They differ in *when* the
grouping happens:

* `nest(prefix, child_router)` — mount an entire existing router
  under a path prefix. Useful when you want a self-contained child
  router that can be tested independently.
* `scope(prefix, |r| { ... })` — declare a temporary prefix and add
  routes inside the closure. Useful for inline grouping inside one
  builder.

```rust
use tako::router::Router;
use tako::responder::Responder;

async fn list_users() -> impl Responder { "users" }
async fn create_user() -> impl Responder { "created" }
async fn dashboard() -> impl Responder { "admin home" }

// child router, mounted with `nest`
let mut api_v1 = Router::new();
api_v1.get("/users", list_users);
api_v1.post("/users", create_user);

let mut root = Router::new();
root.nest("/api/v1", api_v1);

// inline grouping with `scope`
root.scope("/admin", |r| {
  r.get("/", dashboard);
});
```

`nest` clones each child route via `Route::cloned_with_path`, so
re-nesting the same child does not double-stack its middleware. The
child router's *global* middleware chain is prepended to each
nested route's middleware chain at registration time. Route-level
plugins and the child's fallback / error handlers are **not**
inherited — they are router-scoped.

## Method-aware responses

When a path matches but the method does not, Tako returns `405 Method
Not Allowed` with an `Allow` header listing the supported methods.
This is different from v1, which returned plain `404 Not Found`
for the same case.

```text
$ curl -s -X DELETE http://127.0.0.1:8080/users -i
HTTP/1.1 405 Method Not Allowed
allow: GET, POST
content-length: 0
```

## Trailing-slash redirects (`route_with_tsr`)

`route(Method::GET, "/foo", h)` matches only `/foo`. If you also want
`/foo/` to redirect to the canonical form (`308 Permanent Redirect`),
register the route with `route_with_tsr`:

```rust
router.route_with_tsr(Method::GET, "/api", api_handler);
// "/api"  -> handler
// "/api/" -> 308 -> "/api"
```

The root path (`"/"`) is rejected at registration time because it has
no canonical sibling to redirect to.

## Fallback and error handlers

```rust
router.fallback(|_req: tako::types::Request| async {
  (http::StatusCode::NOT_FOUND, "not found")
});

router.client_error_handler(|err| async move {
  (http::StatusCode::BAD_REQUEST, format!("{err}"))
});

router.use_problem_json();   // map all errors through RFC 7807 Problem+JSON
```

`fallback` runs when no route matches at all. `error_handler` /
`client_error_handler` shape how extractor / handler errors hit the
wire. `use_problem_json()` is a convenience preset.

## What changed since 1.x

A short summary; the full table lives in the
[Migration guide](/docs/reference/migration):

| Area                  | 1.x                                                | 2.0                                   |
| --------------------- | -------------------------------------------------- | ------------------------------------- |
| Sub-routing           | `Router::merge` (mutates a shared `Arc<Route>`)    | `nest` / `scope`                      |
| Wrong-method response | `404 Not Found`                                    | `405 + Allow`                         |
| Macro path syntax     | `{id: u64}` only, always materialised as `Params`  | `{id}` and `{id: u64}` both supported |
| Per-router state      | `GLOBAL_STATE` (one slot per `TypeId` per process) | `Router::with_state` (instance-local) |

Server bootstrap and TLS knobs also changed substantially — see the
[Transports overview](/docs/transports) and the migration guide.

Source: https://tako.rust-dd.com/docs/routing