rgoussu@goussu: ~/library/rust/web-frameworks
~/library/rust/web-frameworks cat rocket.md

Rocket deep dive

# The ergonomics pioneer — attribute-macro routing, request guards, fairings, typed config and forms, the async 0.5 rebirth, and its place today.

Conceptsaved 2026-08-09 #rust#web#rocket#ergonomics

Overview

Rocket (2016) was the first Rust web framework with a real developer-experience thesis: routing as attribute macros, request validation as types, sensible defaults, "it should feel like Rails, but checked at compile time". For years it was also the framework that required nightly — its macros leaned on unstable features — which cost it the momentum axum later captured. Rocket 0.5 (2023) landed the rebirth: stable Rust, fully async on Tokio, hyper underneath. It remains the most ergonomic entry point in the ecosystem, with a smaller community and slower cadence than the two leaders.

Key points

  • Routing is declarative: #[get("/users/<id>?<page>")] binds path segments and query params straight into typed function arguments; unmatched types simply don't route (forwarding), turning 404-vs-422 semantics into the type system's job.
  • Request guards are the signature idea: any argument implementing FromRequest — an AdminUser guard that validates the session, a DbConn — runs before the handler; auth as types rather than middleware ordering.
  • Fairings are the lifecycle hooks (attach, request, response) — deliberately not a middleware chain; less composable than tower, simpler to reason about.
  • Batteries included: typed multi-profile config (Rocket.toml + ROCKET_-prefixed env vars), forms with derived validation, templating (Tera and Handlebars via rocket_dyn_templates), TLS, rocket_db_pools for sqlx/deadpool integration.
  • Responders mirror guards: #[derive(Responder)] on enums for typed success/error responses; Result<Json<T>, Status> for the quick path.
  • The trade: highest ergonomics per line and the gentlest learning curve, at the cost of macro-heavy indirection (attribute magic is what axum explicitly avoided), a one-core-team bus factor, and an ecosystem that mostly targets tower now — Rocket integrations are Rocket-specific, the Jakarta-vs-stdlib question in miniature.
  • When it wins: self-contained applications where DX and onboarding dominate — internal tools, teaching, small products; less so services that want deep tower/OpenTelemetry/gRPC ecosystem interop.

Examples

#[get("/users/<id>")]
async fn show_user(id: Uuid, _admin: AdminUser, db: &State<Pool>) -> Result<Json<User>, Status> {
    users::find(db, id).await.map(Json).map_err(|_| Status::NotFound)
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(Db::init())          // fairing
        .mount("/api", routes![show_user])
}

Related