Rocket Web Framework
repository·master·Indexed 12 days ago
https://github.com/rwf2/RocketAn async web framework for Rust focused on usability, security, extensibility, and speed. It provides built-in support for routing, state management, and responders, with official extensions for database pooling (rocket_db_pools, rocket_sync_db_pools), dynamic templating (rocket_dyn_templates), and WebSockets (rocket_ws).
What's inside Rocket
- Rocket is a web framework for Rust designed to be fast, easy, and flexible. It aims to provide guaranteed safety and security while minimizing the amount of boilerplate code a developer needs to write. It is positioned as a modern, flexible alternative to frameworks like Rails, Flask, and Yesod, optimized for the Rust ecosystem.
Explore Rocket application examples
masterThe
examples/directory contains several full-scale applications that demonstrate how to combine Rocket's features into functional software:pastebin: An API-only application that stores pastes on the file system. It demonstrates implementing custom parameter guards (e.g.,PasteId) for parsing and validating identifiers.todo: A web application with a UI for managing tasks. It demonstrates using a SQLite database withdiesel, running automatic migrations at startup, and rendering templates withtera.chat: A real-time multi-room chat app. It demonstrates using Server-Sent Events (SSE) with JavaScript'sEventSource, including handling automatic reconnection with exponential backoff.
Explore Rocket feature examples
masterRocket provides specific examples for individual features to help you understand their implementation details:
config: Extracting values fromFigment, usingAdHoc::config()to store application-specific configuration in managed state, and setting values inRocket.toml.cookies: Implementing client-side message boxes and using private cookies for session-based authentication.databases: Implementing CRUD APIs backed by SQLite usingsqlx,diesel, orrusqlite. Demonstrates usingrocket_db_pools(forsqlx) androcket_sync_db_pools(fordiesel/rusqlite), including automatic migrations.error-handling: Using scoped catchers and customizing error displays.fairings: Creating custom fairings (e.g., aCounter) and usingAdHocfairings.forms: Handling multipart file uploads, ad-hoc validations, field renaming, and staged forms using form context.hello: Core features including route declaration (path and query parameters), mounting, launching, testing, and returning simple responses.manual-routing: Implementing routing without Rocket's codegen (use with caution as it bypasses some automatic security guarantees).responders: Using built-in responders likeStream,Redirect,File,NamedFile,content(for manual Content-Types), andEither. Also shows custom derivedResponderimplementations and usingTempFilefor uploads.serialization: JSON and MessagePack (de)serialization, including UUID parsing support.state: Using request-local state for caching per-request operations and managed state for global data like hit counters or concurrent queues.static-files: Serving static files usingFileServeror manual safe implementations.templating: Usingcontribtemplates support withhandlebarsortera.testing: Using Rocket'slocallibraries and theasyncClientto test applications.tls: Configuring TLS with various key pair types.upgrade: Implementing WebSocket support using the connection upgrade API andtungstenite.
Parse nested structures and collections
masterRocket supports complex, nested form data using dot notation (
.) or bracket notation ([]).Nesting
To parse nested structs, use
parent.childorparent[child]syntax.Vectors
To parse into a
Vec<T>, usename[$k]where$kis a key. If the key is the same as the previous one, the value is pushed to the current element; otherwise, a new element is created.name[](empty key) always creates a new element.Maps
To parse into a
HashMap<K, V>, usename[$key]. Unlike vectors, the key$keyis semantically meaningful and is stored in the map. If the key is a complex type (a struct), usename[k:$key].fieldto define the key's components.// Nesting example #[derive(FromForm)] struct MyForm<'r> { owner: Person<'r>, pet: Pet<'r>, } // Form fields: owner.name=Bob&pet.name=Sally // Vector example #[derive(FromForm)] struct MyForm { numbers: Vec<usize>, } // Form fields: numbers[]=1&numbers[]=2 // Map example #[derive(FromForm)] struct MyForm { ids: HashMap<String, usize>, } // Form fields: ids[a]=1&ids[b]=2Use Managed State instead of global state
masterAvoid using global state (e.g.,lazy_static!). Instead, use Rocket's [managed state] mechanism. Managed state allows for better testability, the ability to run the application on different threads with different state, and makes a route's state dependencies explicit in its signature.Scope Catchers using Path Prefixes
masterWhen registering catchers with
.register(base, ...), thebasedetermines which requests the catcher handles. A catcher's base must be a prefix of the erroring request. If multiple catchers match, the one with the longest base takes precedence.#[catch(404)] fn general_not_found() -> &'static str { "General 404" } #[catch(404)] fn foo_not_found() -> &'static str { "Foo 404" } #[launch] fn rocket() -> _ { rocket::build() .register("/", catchers![general_not_found]) .register("/foo", catchers![foo_not_found]) } // Requests to /foo/bar will trigger 'foo_not_found'. // Requests to /bar will trigger 'general_not_found'.Reinterpret request methods via HTML forms
masterTo support non-POSTmethods (likePUTorDELETE) from standard HTML forms, Rocket supports method reinterpretation. If aPOSTrequest has aContent-Typeofapplication/x-www-form-urlencodedand the first field in the body is named_methodwith a valid HTTP method name as its value, Rocket will treat the request as that method instead ofPOST.Implement load balancing and DDoS mitigation
masterRocket does not include built-in DDoS mitigation. For production, you should place your Rocket application behind a load balancer or reverse proxy.
- Managed Environments (Kubernetes, Heroku, Google Cloud Run): Load balancing and DDoS protection are typically handled automatically by the platform.
- Self-Managed Environments (VPS, Bare Metal): It is recommended to use a mature reverse proxy such as HAProxy or NGINX in front of your Rocket instance.
Understand Rocket's core design philosophies
masterRocket's architecture is built upon three primary pillars:
- Security, correctness, and developer experience: The framework is designed so that the easiest way to write code (the path of least resistance) is also the most secure and correct way. It aims to provide security without increasing cognitive overhead.
- Typed and self-contained request handling: To bridge the gap between untyped HTTP/web protocols and Rust's type system, Rocket automatically converts request data into native types. Handlers are implemented as regular functions with regular arguments, maintaining zero global state.
- Pluggable components (No forced decisions): Rocket follows a modular approach. While it provides official support for templates, serialization, and sessions, these are all optional and swappable components. You are not forced to use a specific implementation if you prefer another.
How request forwarding works in Rocket
masterWhen a parameter type mismatch occurs (e.g., a route expects a
u8but receives a string), Rocket forwards the request to the next matching route. This process continues in increasing rank order until a route succeeds or no more matching routes exist. If no routes match, the error catcher associated with the last forwarding guard's status is called.To prevent a route from forwarding on a type mismatch, you can use
ResultorOptiontypes in the handler signature. UsingResult<T, E>orOption<T>catches the failure within that specific route instead of triggering a forward to the next rank.#[get("/hello/<name>/<age>/<cool>")] fn hello(name: &str, age: u8, cool: bool) { /* ... */ } // If 'age' is not a u8, Rocket forwards to the next route with a matching path. // To catch the error instead of forwarding, use: #[get("/hello/<name>/<age>/<cool>")] fn hello(name: &str, age: Result<u8, &str>, cool: bool) { /* ... */ }Use Managed State to maintain application-wide data
masterManaged state allows you to maintain data that is accessible throughout your entire application. Rocket manages state on a per-type basis: you can only manage at most one value of any given type.
Requirements:
- All managed state must be thread-safe (must implement
Send+Sync) because Rocket parallelizes request handling.
Workflow:
- Add State: Call
.manage(value)on yourRocketinstance during initialization. Each call must use a different type. - Retrieve State: Add
&State<T>as an argument to any request handler, whereTis the type of the managed value.
use std::sync::atomic::AtomicUsize; struct HitCount { count: AtomicUsize } // 1. Adding State rocket::build().manage(HitCount { count: AtomicUsize::new(0) }); // 2. Retrieving State use rocket::State; #[get("/count")] fn count(hit_count: &State<HitCount>) -> String { let current_count = hit_count.count.load(std::sync::atomic::Ordering::Relaxed); format!("Number of visits: {}", current_count) }- All managed state must be thread-safe (must implement
Handle service management and graceful shutdown
masterTo deploy updates without dropping requests, your application should implement graceful shutdown. Rocket listens for termination signals (like
SIGTERMin Kubernetes) by default.To ensure a clean exit, your application should:
- Use Rocket's
Shutdownfuture or shutdown fairings to clean up resources (database connections, file handles, etc.) before the process terminates. - Ensure your environment's termination signal is recognized by Rocket. If your environment uses non-standard signals, you may need to configure triggers in your shutdown configuration.
- In self-managed environments, use a service manager like
systemdalongside a reverse proxy to manage the application lifecycle.
- Use Rocket's