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);