Architecture
The big picture of tako — Router, handlers, extractors, middleware, and responders in the core, with the server crate driving transports.
Architecture
Tako separates what a service does from how it is served. The framework
primitives — routing, extraction, middleware, responses, state — live in
tako-rs-core and are independent of any transport. The tako-rs-server crate
drives concrete transports and feeds requests into that core. The result is one
application model that works the same whether the bytes arrive over HTTP/1.1,
HTTP/2, HTTP/3, a raw TCP socket, or a Unix socket.
Layers
| Layer | Crate | Responsibility |
|---|---|---|
| Transports | tako-rs-server, tako-rs-streams | Accept connections, parse protocol frames, hand each request to the router |
| Routing | tako-rs-core (router) | Match (Method, path) to a handler; run the middleware chain; answer 405 / fallback |
| Middleware | tako-rs-core (middleware), tako-rs-plugins | Wrap the handler — auth, CORS, compression, rate limiting, metrics |
| Extraction | tako-rs-core (extractors), tako-rs-extractors | Turn raw request parts into typed handler arguments |
| Handler | your code | async fn from extracted arguments to a Responder |
| Response | tako-rs-core (responder) | Convert the handler's return value into a Response |
The core types
A handful of types from tako-rs-core define the whole surface:
Routeris the dispatch type. It maps(Method, path)pairs to handlers, runs global and per-route middleware, and produces a405 Method Not Allowed(with a populatedAllowheader) when the path matches but the method does not. Path syntax ismatchit-compatible.RequestandResponseare thin aliases —http::Request<TakoBody>andhttp::Response<TakoBody>— so the wholehttpcrate ecosystem composes directly.TakoBodyis Tako's streaming body type.- A handler is any
async fnwhose arguments implementFromRequest/FromRequestPartsand whose return type implementsResponder. TheHandlertrait is implemented automatically; you never write it by hand. Responderconverts a return value into aResponse. Blanket impls cover&str,String,Bytes,Json<T>,(StatusCode, T),StatusCode, andResult<T, E>.IntoMiddlewareplus theNextchain compose request processing. Middleware takes(Request, Next)and returns aResponse; callingnext.run(req)advances to the next middleware and eventually the handler.
use tako::middleware::Next;
use tako::types::{Request, Response};
async fn logging(req: Request, next: Next) -> Response {
println!("{} {}", req.method(), req.uri());
next.run(req).await
}State model
Application state is shared with handlers in two scopes. Global state is
keyed by type and read with the State<T> extractor. Per-router state is
instance-local: Router::with_state attaches a typed container to one router
instance, replacing the v1 process-global slot. The
State page covers both; per-router state is what lets two
independently nested routers carry different configuration.
How transports plug in
The server crate is the only layer that touches sockets. tako::serve builds a
default Server and accepts connections on a TcpListener, dispatching each
request through the same Router. Other transports follow the identical shape
through dedicated entry points — serve_tls, serve_h2c, serve_h3,
server_tcp, server_udp, server_unix, and the compio variants — all of
which drive the unchanged core router. Streaming and upgrade transports
(WebSocket, SSE, WebTransport, file streaming) live in tako-rs-streams and
hook into the same request flow.
This is the payoff of the layering: routing, middleware, extraction, and responses are written once and reused across every transport. To follow a single request through all of these layers in order, read the request lifecycle. For the crate-by-crate breakdown, see project layout.