Quickstart
Add the tako-rs dependency, write a handler, register a GET route on Router, and start it with Server::builder and a TcpListener.
This walks through installing Tako, writing your first handler, and running it
on a local TCP listener. It mirrors the
examples/hello-world
crate in the repository.
1. Add the dependency
[dependencies]
tako-rs = "2"
tokio = { version = "1", features = ["full"] }
anyhow = "1"The package is published as tako-rs; everything is re-exported under the
tako::* path. The default feature set covers HTTP/1.1, raw TCP, and Unix
sockets on Tokio; WebSocket (ws), SSE (sse), UDP (udp), PROXY protocol
(proxy-protocol), HTTP/2, HTTP/3, and TLS are opt-in features. See
Installation for the runtime choice and
opt-in feature flags.
2. Write the server
use anyhow::Result;
use tako::Method;
use tako::responder::Responder;
use tako::router::Router;
use tokio::net::TcpListener;
async fn hello_world() -> impl Responder {
"Hello, World!".into_response()
}
#[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, "/", hello_world);
tako::Server::builder()
.build()
.spawn_http(listener, router)
.result()
.await
.expect("HTTP server failed");
Ok(())
}3. Run it
cargo runThen, in another terminal:
curl http://127.0.0.1:8080/
# Hello, World!What the code does
A handler is any async fn whose arguments implement FromRequest /
FromRequestParts and whose return type implements Responder. The hello-world
handler takes zero arguments, so it extracts nothing from the request:
async fn hello_world() -> impl Responder {
"Hello, World!".into_response()
}Router::new() produces an empty router. route(Method::GET, "/", handler)
registers a handler against a (method, path) pair. Convenience shorthands
exist too — router.get(path, handler), plus .post, .put, .patch,
.delete, .head, and .options:
let mut router = Router::new();
router.route(Method::GET, "/", hello_world);
// equivalently: router.get("/", hello_world);Server::builder().build().spawn_http(listener, router) returns a server handle.
Await result() to observe startup or serving errors, or call shutdown(timeout)
to stop acceptance and drain active requests.
Adding extractors
Handlers compose by taking more arguments. Tako runs the extractor for each argument before invoking the handler:
use serde::Deserialize;
use tako::Method;
use tako::extractors::json::Json;
use tako::extractors::path::Path;
use tako::responder::Responder;
use tako::router::Router;
#[derive(Deserialize)]
struct UserPath { id: u64 }
#[derive(Deserialize)]
struct CreateUser { name: String }
async fn get_user(Path(p): Path<UserPath>) -> impl Responder {
format!("user_id={}", p.id)
}
async fn create_user(Json(body): Json<CreateUser>) -> impl Responder {
format!("created: {}", body.name)
}
let mut router = Router::new();
router.route(Method::GET, "/users/{id}", get_user);
router.route(Method::POST, "/users", create_user);Return types
Anything implementing Responder can be returned from a handler. The blanket
impls cover the common shapes:
&'static str/String—text/plainbodyBytes/Vec<u8>— raw bodyJson<T>— JSON body withContent-Type: application/json(StatusCode, T)— status plus body(StatusCode, HeaderMap, T)— status, headers, and bodyStatusCodealone — empty body, just the status lineResult<T, E>where both arms implementResponder— the preferred shape for fallible handlers
Next steps
- Routing — path parameters, nesting, scopes, typed slots.
- Extractors — the full catalog of request extractors.
- State — share configuration and dependencies with handlers.
- Middleware — auth, CORS, compression, metrics, rate limiting.
Last updated on