Extractors are typed handler arguments that read specific shapes out of an incoming request (such as JSON, query parameters, or headers) and surface them as typed variables.
To use an extractor, include it as a parameter in your asynchronous handler function. You typically destructure the extractor wrapper to access the underlying data.
Key Rules:
- Body Extractors (implementing
FromRequest) consume the request body. Because the body can only be read once, these must be the last argument in your handler's parameter list. - Parts Extractors (implementing
FromRequestParts) only access headers or the URI. These can appear in any order and any number of times before the body extractor. - Error Handling: If an extractor fails (e.g., malformed JSON or missing parameters), the handler is not called, and the extractor's error is converted into a
Responder (typically returning a 400 or 401 status code).
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;
// Example: Using multiple extractors (Path, Query, and State)
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)
}
// Example: Using Path and Json (Json is the body extractor, so it is last)
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::serve(listener, router).await;
Ok(())
}