# Extractors

> Typed handler arguments that read shape out of a request — JSON, form, query, path, headers, cookies, JWT, and more.

Extractors read shape out of a request and surface it as typed handler
arguments. Any type implementing
[`FromRequest`](https://docs.rs/tako-rs/latest/tako/extractors/trait.FromRequest.html)
(consumes the body) or
[`FromRequestParts`](https://docs.rs/tako-rs/latest/tako/extractors/trait.FromRequestParts.html)
(headers / URL only) can be used as a handler parameter. A handler
may take any number of extractors; Tako runs them in order before
calling the handler.

```rust
use serde::{Deserialize, Serialize};
use tako::Method;
use tako::extractors::json::Json;
use tako::extractors::path::Path;
use tako::extractors::query::Query;
use tako::extractors::state::State;
use tako::responder::Responder;
use tako::router::Router;

#[derive(Deserialize)]
struct ListQuery { page: u32, per_page: u32 }

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

#[derive(Deserialize, Serialize)]
struct CreatePost { title: String, body: String }

#[derive(Clone)]
struct Db; // imagine a real pool here

async fn list_posts(
  Path(UserPath { id }): Path<UserPath>,
  Query(q): Query<ListQuery>,
  State(_db): State<Db>,
) -> impl Responder {
  format!("user={id}, page={}, per_page={}", q.page, q.per_page)
}

async fn create_post(
  Path(UserPath { id }): Path<UserPath>,
  Json(p): Json<CreatePost>,
) -> Json<CreatePost> {
  println!("creating post for user={id}: {:?}", p.title);
  Json(p)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await?;

  let mut router = Router::new();
  router.with_state(Db);
  router.route(Method::GET,  "/users/{id}/posts", list_posts);
  router.route(Method::POST, "/users/{id}/posts", create_post);

  tako::Server::builder().build().spawn_http(listener, router)
    .result().await.expect("HTTP server failed");
  Ok(())
}
```

## How binding works

Each extractor is a wrapper type you destructure in the argument
position — `Json(payload): Json<T>`, `Path(p): Path<T>`, and so on. The
body extractors (`FromRequest`) consume the request body and must come
last in the argument list, because the body can only be read once. The
parts extractors (`FromRequestParts`) only touch headers and the URI,
so any number of them can run before the body extractor.

If extraction fails — a malformed JSON body, a missing required query
parameter, a bad `Authorization` header — the extractor's error type is
turned into a `Responder` (usually a `400` or `401`) and the handler is
never called. Route-level error handlers and
[`use_problem_json()`](/docs/routing) can reshape those errors before
they hit the wire.

Source: [examples/extractors-multi/src/main.rs](https://github.com/rust-dd/tako/blob/main/examples/extractors-multi/src/main.rs)

```rust
use anyhow::Result;
use serde::Deserialize;
use serde::Serialize;
use tako::Method;
use tako::extractors::json::Json;
use tako::extractors::params::Params;
use tako::extractors::query::Query;
use tako::router::Router;
use tokio::net::TcpListener;

#[derive(Deserialize)]
struct Pagination {
  page: u32,
  per_page: u32,
}

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

#[derive(Deserialize, Serialize, Clone)]
struct CreateUser {
  name: String,
  email: String,
}

#[derive(Serialize)]
struct Created {
  id: u64,
  name: String,
  email: String,
}

// GET /users/{id}/posts?per_page=10&page=2
// Demonstrates multiple extractors: Params + Query
async fn list_user_posts(Params(user): Params<UserPath>, Query(p): Query<Pagination>) -> String {
  format!(
    "user_id={}, page={}, per_page={}",
    user.id, p.page, p.per_page
  )
}

// POST /users with JSON body {"name":"...","email":"..."}
// Demonstrates a body extractor
async fn create(Json(user): Json<CreateUser>) -> Json<Created> {
  // Normally you'd persist the user; here we just echo back with an id
  Json(Created {
    id: 1,
    name: user.name,
    email: user.email,
  })
}

#[tokio::main]
async fn main() -> Result<()> {
  let listener = TcpListener::bind("127.0.0.1:8080").await?;

  let mut router = Router::new();
  router.route(Method::GET, "/users/{id}/posts", list_user_posts);
  router.route(Method::POST, "/users", create);

  tako::Server::builder()
    .build()
    .spawn_http(listener, router)
    .result()
    .await
    .expect("HTTP server failed");

  Ok(())
}
```

## Catalog

Tako ships 22+ bundled extractors. They fall into five groups, each
with its own reference page.

### Body — `/docs/extractors/body`

| Extractor                              | Description                                 |
| -------------------------------------- | ------------------------------------------- |
| [`Json<T>`](/docs/extractors/body)     | JSON body (with optional SIMD acceleration) |
| [`Form<T>`](/docs/extractors/body)     | URL-encoded form body                       |
| [`Bytes`](/docs/extractors/body)       | Raw request body stream                     |
| [`Protobuf<T>`](/docs/extractors/body) | Protocol Buffers body (feature `protobuf`)  |
| [`SimdJson<T>`](/docs/extractors/body) | Force SIMD JSON parsing (feature `simd`)    |
| [`Multipart`](/docs/extractors/body)   | Multipart form data (feature `multipart`)   |

### Request metadata — `/docs/extractors/request-meta`

| Extractor                                         | Description                 |
| ------------------------------------------------- | --------------------------- |
| [`Query<T>`](/docs/extractors/request-meta)       | URL query parameters        |
| [`Path<T>`](/docs/extractors/request-meta)        | Typed route path parameters |
| [`Params<T>`](/docs/extractors/request-meta)      | Dynamic path params map     |
| [`HeaderMap`](/docs/extractors/request-meta)      | Full request headers        |
| [`Accept`](/docs/extractors/request-meta)         | Content negotiation         |
| [`AcceptLanguage`](/docs/extractors/request-meta) | Language negotiation        |
| [`Range`](/docs/extractors/request-meta)          | HTTP Range header           |
| [`IpAddr`](/docs/extractors/request-meta)         | Client IP address           |

### Auth — `/docs/extractors/auth`

| Extractor                                         | Description                           |
| ------------------------------------------------- | ------------------------------------- |
| [`Basic`](/docs/extractors/auth)                  | HTTP Basic credentials (`BasicAuth`)  |
| [`Bearer`](/docs/extractors/auth)                 | Bearer token (`BearerAuth`)           |
| [`ApiKey`](/docs/extractors/auth)                 | API key from header or query          |
| [`JwtClaimsUnverified<T>`](/docs/extractors/auth) | JWT claims, signature **not** checked |
| [`JwtClaimsVerified<T>`](/docs/extractors/auth)   | Signature-verified JWT claims         |

### Cookies — `/docs/extractors/cookies`

| Extractor                                   | Description                             |
| ------------------------------------------- | --------------------------------------- |
| [`CookieJar`](/docs/extractors/cookies)     | Cookie reading / writing                |
| [`CookieSigned`](/docs/extractors/cookies)  | HMAC-signed cookies (`SignedCookieJar`) |
| [`CookiePrivate`](/docs/extractors/cookies) | Encrypted cookies (`PrivateCookieJar`)  |

### State — `/docs/state`

| Extractor                 | Description                           |
| ------------------------- | ------------------------------------- |
| [`State<T>`](/docs/state) | Shared application state, as `Arc<T>` |

## Beyond the catalog

The crate also bundles `QueryMulti<T>` (repeated keys), `RawPath` /
`RawQuery`, `MatchedPath`, `OriginalUri`, `Host`, `Scheme`,
`TypedHeader<H>`, `Extension<T>`, `ConnectInfo<T>`, `ContentLengthLimit<T, N>`,
and `Validated<T>` (behind the `validator` / `garde` features). The
`zero-copy-extractors` feature enables borrowing variants of the body and
header extractors for hot-path handlers. See the
[crate rustdoc](https://docs.rs/tako-rs/latest/tako/extractors/) for the
complete list.

## Writing an extractor

The last handler argument must implement `FromRequest`; earlier ones must
implement `FromRequestParts`. An extractor that only reads headers, the URI, or
extensions implements both, so it fits any position. Set `ENTRIES` to the
framework entries it reads; this one reads none:

```rust
use http::request::Parts;
use tako::StatusCode;
use tako::extractors::Entries;
use tako::extractors::FromRequest;
use tako::extractors::FromRequestParts;
use tako::header::HeaderMap;
use tako::types::Request;

struct RequestId(String);

fn request_id(headers: &HeaderMap) -> Result<RequestId, StatusCode> {
  headers
    .get("x-request-id")
    .and_then(|value| value.to_str().ok())
    .map(|id| RequestId(id.to_owned()))
    .ok_or(StatusCode::BAD_REQUEST)
}

impl<'a> FromRequestParts<'a> for RequestId {
  type Error = StatusCode;
  const ENTRIES: Entries = Entries::NONE;

  async fn from_request_parts(parts: &'a mut Parts) -> Result<Self, Self::Error> {
    request_id(&parts.headers)
  }
}

impl<'a> FromRequest<'a> for RequestId {
  type Error = StatusCode;
  const ENTRIES: Entries = Entries::NONE;

  async fn from_request(req: &'a mut Request) -> Result<Self, Self::Error> {
    request_id(req.headers())
  }
}
```

The router can attach these entries to a request's extensions:

| Entry                   | Attaches                             | Read by                                                               |
| ----------------------- | ------------------------------------ | --------------------------------------------------------------------- |
| `Entries::CONN`         | `ConnInfo` and the peer `SocketAddr` | `ConnectInfo<T>`, `IpAddr`                                            |
| `Entries::MATCHED_PATH` | `MatchedPath`                        | `MatchedPath`                                                         |
| `Entries::PARAMS`       | the matched path parameters          | `Path<T>`, `Params<T>`, `TypedParams<T>`                              |
| `Entries::STATE`        | router and route state               | `State<T>`, `IpAddr`                                                  |
| `Entries::BODY_LIMIT`   | the body size limit                  | `Json<T>`, `Form<T>`, `Bytes`, `String`, `Protobuf<T>`, `SimdJson<T>` |
| `Entries::SIMD_JSON`    | the route's SIMD JSON mode           | `Json<T>`                                                             |

A route without middleware or other per-request hooks gets only the entries its
handler's extractors list, which saves work on every request; all other requests
get every entry. Combine entries with `Entries::CONN.union(Entries::STATE)`.
`ENTRIES` defaults to `Entries::ALL`, which is always correct. An extractor that
reads an entry it does not list finds it missing on those routes.

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