Recipe

Tower middleware primer

Tower is the composable middleware crate behind Axum, Hyper, and Tonic. This primer walks through the three load-bearing concepts you need before wiring your first layer into a Meridian gateway: the Service trait, the Layer trait, and how to compose them with ServiceBuilder.

1. The Service trait is just async fn

A Tower Service<Request> is an async function from request to response, plus a readiness check. Anything that implements it can sit anywhere in a Tower stack: an HTTP handler, a database pool, a retry budget, a rate limiter. The uniform shape is what makes composition cheap.

2. Layers wrap services into new services

A Layer takes a service and returns a new service. Timeouts, tracing, auth, and load-shedding all ship as layers. Because every layer output is itself a service, you can stack them indefinitely without changing the downstream call site.

3. ServiceBuilder is the ergonomic stack

ServiceBuilder stacks layers outside-in so the code reads in the order requests flow. Below is the canonical pattern Meridian uses for its model-router edge:

use tower::ServiceBuilder;
use tower_http::trace::TraceLayer;
use std::time::Duration;

let svc = ServiceBuilder::new()
    .layer(TraceLayer::new_for_http())
    .timeout(Duration::from_secs(30))
    .concurrency_limit(256)
    .rate_limit(1000, Duration::from_secs(1))
    .service(router);