rocket

Rocket Framework Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rocket" with this command: npx skills add ar4mirez/samuel/ar4mirez-samuel-rocket

Rocket Framework Guide

Applies to: Rocket 0.5+, Rust Web APIs, Type-Safe Web Applications Use with: .claude/skills/rust-guide/SKILL.md

Overview

Rocket is a Rust web framework focused on ease of use, developer experience, and type safety. It uses Rust's type system to ensure correctness at compile time, with powerful features like request guards, fairings (middleware), and derive macros for minimal boilerplate.

When to Use Rocket

  • Type-Safe APIs: Maximum compile-time guarantees

  • Developer Experience: Clean, expressive syntax with minimal boilerplate

  • Rapid Prototyping: Quick to start; attribute macros handle routing

  • Form Handling: Excellent support for HTML forms and multipart uploads

  • Traditional Web Apps: Both APIs and server-rendered HTML

When NOT to Use Rocket

  • Async-Heavy Workloads: Consider Axum for pure async performance

  • Minimal Dependencies: Rocket has heavier compile times

  • Maximum Control: Use Axum/Actix for fine-grained middleware control

Project Structure

myproject/ ├── Cargo.toml ├── Rocket.toml # Rocket configuration (per-profile) ├── src/ │ ├── main.rs # Entry point (#[launch] or #[rocket::main]) │ ├── lib.rs # Library exports │ ├── routes/ │ │ ├── mod.rs # Route mounting functions │ │ ├── users.rs # User handlers │ │ └── health.rs # Health checks │ ├── models/ │ │ ├── mod.rs │ │ └── user.rs # Domain + request/response models │ ├── guards/ # Request guards (auth, rate-limit, etc.) │ │ ├── mod.rs │ │ └── auth.rs │ ├── fairings/ # Middleware (logging, CORS, timing) │ │ ├── mod.rs │ │ └── logging.rs │ ├── db/ │ │ ├── mod.rs # Database pool definition │ │ └── pool.rs │ ├── services/ # Business logic layer │ │ ├── mod.rs │ │ └── user_service.rs │ ├── error.rs # AppError + catchers │ └── config.rs # Custom config extraction ├── tests/ │ └── api_tests.rs # Integration tests via Client ├── migrations/ └── static/ # Static file serving

Dependencies

[dependencies] rocket = { version = "0.5", features = ["json", "secrets"] } rocket_db_pools = { version = "0.1", features = ["sqlx_postgres"] } sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "chrono", "uuid"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" validator = { version = "0.16", features = ["derive"] } jsonwebtoken = "9" bcrypt = "0.15" chrono = { version = "0.4", features = ["serde"] } uuid = { version = "1", features = ["v4", "serde"] } thiserror = "1.0" dotenvy = "0.15" tokio = { version = "1", features = ["full"] }

[dev-dependencies] rocket = { version = "0.5", features = ["json", "secrets"] }

Application Entry Point

#[macro_use] extern crate rocket;

use rocket::{Build, Rocket}; use rocket_db_pools::Database;

#[launch] fn rocket() -> Rocket<Build> { dotenvy::dotenv().ok();

rocket::build()
    .attach(DbPool::init())
    .attach(RequestLogger)
    .mount("/api", routes::api_routes())
    .mount("/health", routes::health_routes())
    .register("/", catchers![
        error::not_found,
        error::internal_error,
        error::unauthorized,
        error::bad_request,
    ])

}

Configuration (Rocket.toml)

[default] address = "0.0.0.0" port = 8000 workers = 16 keep_alive = 5 log_level = "normal" limits = { form = "64 kB", json = "1 MiB" }

[default.databases.db_pool] url = "postgres://user:password@localhost/mydb" min_connections = 5 max_connections = 20 connect_timeout = 5 idle_timeout = 300

[debug] log_level = "debug"

[release] secret_key = "generate-a-256-bit-base64-key" log_level = "critical"

Custom config values via environment or Rocket.toml:

use rocket::serde::Deserialize;

#[derive(Debug, Deserialize)] #[serde(crate = "rocket::serde")] pub struct AppConfig { pub jwt_secret: String, pub jwt_expiration_hours: i64, }

impl Default for AppConfig { fn default() -> Self { Self { jwt_secret: std::env::var("JWT_SECRET") .unwrap_or_else(|| "development-secret-key".to_string()), jwt_expiration_hours: std::env::var("JWT_EXPIRATION_HOURS") .unwrap_or_else(|| "24".to_string()) .parse() .unwrap_or(24), } } }

Routing Macros

Rocket uses attribute macros on handler functions. The macro defines the HTTP method, path, and dynamic segments.

#[get("/")] // GET / #[post("/users", data = "<input>")]// POST /users with body #[put("/users/<id>", data = "<d>")] // PUT /users/:id with body #[delete("/users/<id>")] // DELETE /users/:id #[get("/users?<limit>&<offset>")] // GET /users?limit=&offset=

Mount routes via Vec<Route> :

pub fn api_routes() -> Vec<Route> { routes![register, login, get_user, list_users, delete_user] }

Request Guards

Request guards implement FromRequest and run before the handler. They are Rocket's primary mechanism for authentication, authorization, and request validation.

#[derive(Debug)] pub struct AuthUser { pub user_id: Uuid, pub email: String, pub role: String, }

#[rocket::async_trait] impl<'r> FromRequest<'r> for AuthUser { type Error = AppError;

async fn from_request(request: &#x26;'r Request&#x3C;'_>) -> Outcome&#x3C;Self, Self::Error> {
    let token = match request.headers().get_one("Authorization") {
        Some(h) if h.starts_with("Bearer ") => &#x26;h[7..],
        _ => return Outcome::Error((
            Status::Unauthorized,
            AppError::Unauthorized("Missing Authorization header".into()),
        )),
    };

    // Decode and validate JWT, return Outcome::Success or Outcome::Error
    // ...
}

}

Compose guards for role-based access:

pub struct AdminUser(pub AuthUser);

#[rocket::async_trait] impl<'r> FromRequest<'r> for AdminUser { type Error = AppError;

async fn from_request(request: &#x26;'r Request&#x3C;'_>) -> Outcome&#x3C;Self, Self::Error> {
    let user = match AuthUser::from_request(request).await {
        Outcome::Success(u) => u,
        Outcome::Error(e) => return Outcome::Error(e),
        Outcome::Forward(s) => return Outcome::Forward(s),
    };
    if user.role != "admin" {
        return Outcome::Error((
            Status::Forbidden,
            AppError::Unauthorized("Admin access required".into()),
        ));
    }
    Outcome::Success(AdminUser(user))
}

}

Optional auth: wrap in Option<AuthUser> or create an OptionalUser guard.

Responders and Error Handling

Implement Responder for custom error types to return structured JSON errors:

#[derive(Error, Debug)] pub enum AppError { #[error("Not found: {0}")] NotFound(String), #[error("Unauthorized: {0}")] Unauthorized(String), #[error("Validation: {0}")] Validation(String), #[error("Database error: {0}")] Database(#[from] sqlx::Error), #[error("Internal: {0}")] Internal(String), }

impl<'r> Responder<'r, 'static> for AppError { fn respond_to(self, request: &'r Request<'>) -> response::Result<'static> { let (status, body) = match &self { AppError::NotFound(m) => (Status::NotFound, json!({"error": m})), AppError::Unauthorized(m) => (Status::Unauthorized, json!({"error": m})), AppError::Validation(m) => (Status::BadRequest, json!({"error": m})), AppError::Database() => (Status::InternalServerError, json!({"error": "database error"})), AppError::Internal(_) => (Status::InternalServerError, json!({"error": "internal error"})), }; Response::build_from(Json(body).respond_to(request)?) .status(status) .ok() } }

Register catchers for unhandled status codes:

#[catch(404)] pub fn not_found() -> Json<Value> { Json(json!({"error": "not found"})) }

#[catch(500)] pub fn internal_error() -> Json<Value> { Json(json!({"error": "internal error"})) }

#[catch(401)] pub fn unauthorized() -> Json<Value> { Json(json!({"error": "unauthorized"})) }

Fairings (Middleware)

Fairings attach cross-cutting behavior to the request/response lifecycle.

pub struct RequestLogger;

#[rocket::async_trait] impl Fairing for RequestLogger { fn info(&self) -> Info { Info { name: "Request Logger", kind: Kind::Request | Kind::Response } }

async fn on_request(&#x26;self, request: &#x26;mut Request&#x3C;'_>, _: &#x26;mut Data&#x3C;'_>) {
    request.local_cache(|| Instant::now());
}

async fn on_response&#x3C;'r>(&#x26;self, request: &#x26;'r Request&#x3C;'_>, response: &#x26;mut Response&#x3C;'r>) {
    let start = request.local_cache(|| Instant::now());
    println!("{} {} {} - {:?}", request.method(), request.uri(), response.status().code, start.elapsed());
}

}

CORS fairing pattern:

pub struct Cors;

#[rocket::async_trait] impl Fairing for Cors { fn info(&self) -> Info { Info { name: "CORS", kind: Kind::Response } }

async fn on_response&#x3C;'r>(&#x26;self, _: &#x26;'r Request&#x3C;'_>, response: &#x26;mut Response&#x3C;'r>) {
    response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
    response.set_header(Header::new("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"));
    response.set_header(Header::new("Access-Control-Allow-Headers", "Content-Type, Authorization"));
}

}

State Management

Database Pool (rocket_db_pools)

use rocket_db_pools::{sqlx, Database, Connection};

#[derive(Database)] #[database("db_pool")] pub struct DbPool(sqlx::PgPool);

pub type DbConn = Connection<DbPool>;

Use in handlers:

#[get("/users/<id>")] pub async fn get_user(mut db: DbConn, id: &str) -> Result<Json<User>, AppError> { let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1") .bind(id) .fetch_optional(&mut **db) .await? .ok_or_else(|| AppError::NotFound("user not found".into()))?; Ok(Json(user)) }

Managed State

// Attach in build rocket::build().manage(AppConfig::default())

// Extract in handler #[get("/info")] fn info(config: &State<AppConfig>) -> String { format!("JWT expiry: {}h", config.jwt_expiration_hours) }

Form Handling

#[derive(FromForm)] pub struct UploadForm<'r> { pub name: String, pub description: Option<String>, pub file: TempFile<'r>, }

#[post("/upload", data = "<form>")] pub async fn upload(mut form: Form<UploadForm<'>>, auth: AuthUser) -> Result<Json<Value>, AppError> { let filename = format!("uploads/{}{}", auth.user_id, form.name); form.file.persist_to(&filename).await .map_err(|e| AppError::Internal(e.to_string()))?; Ok(Json(json!({"status": "uploaded", "filename": filename}))) }

Multipart with built-in validation:

#[derive(FromForm)] pub struct ProfileForm<'r> { #[field(validate = len(1..100))] pub name: String, #[field(validate = contains('@'))] pub email: String, #[field(validate = len(..1_000_000))] pub avatar: Option<TempFile<'r>>, }

Commands

cargo run # Start dev server cargo build --release # Production build cargo test # All tests cargo test test_health_check # Specific test cargo fmt # Format cargo clippy # Lint cargo check # Fast type-check cargo doc --open # Generate docs ROCKET_LOG_LEVEL=debug cargo run # Debug logging

Best Practices

DO

  • Use request guards for authentication/authorization

  • Implement Responder for custom error types

  • Use fairings for cross-cutting concerns (logging, CORS, timing)

  • Leverage Rocket's type system for compile-time validation

  • Use rocket_db_pools for managed database connections

  • Configure via Rocket.toml with per-profile settings

  • Use derive macros (FromForm , FromRequest ) to reduce boilerplate

  • Test with rocket::local::asynchronous::Client

  • Register catchers for all common HTTP error codes

DON'T

  • Use unwrap() /expect() in handler code

  • Ignore validation errors from form or JSON input

  • Store secrets in Rocket.toml (use environment variables)

  • Block async handlers with synchronous operations

  • Skip error catchers for 404, 401, 500

  • Use global mutable state when State<T> or request-local cache suffices

  • Forget to attach database pool via .attach(DbPool::init())

Advanced Patterns

For detailed handler examples, database integration, authentication flows, form handling, and testing patterns, see:

  • references/patterns.md -- CRUD handlers, Diesel/SQLx, JWT auth, testing with Client

External References

  • Rocket Documentation

  • Rocket Guide

  • Rocket API Reference

  • rocket_db_pools

  • Rocket GitHub

  • Rocket Examples

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

actix-web

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

blazor

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi

No summary provided by upstream source.

Repository SourceNeeds Review