Guide

The ins and outs of Rocket, in detail.

Overview#

Rocket provides primitives to build web servers and applications with Rust: the rest is up to you. In short, Rocket provides routing, pre-processing of requests, and post-processing of responses. Your application code instructs Rocket on what to pre-process and post-process and fills the gaps between pre-processing and post-processing.

Lifecycle#

Rocket’s main task is to listen for incoming web requests, dispatch the request to the application code, and return a response to the client. We call the process that goes from request to response: the lifecycle. We summarize the lifecycle as the sequence of steps:

  1. Routing

    Rocket parses an incoming HTTP request into native structures that your code operates on indirectly. Rocket determines which request handler to invoke by matching against route attributes declared by your application.

  2. Validation

    Rocket validates the incoming request against types and request guards present in the matched route. If validation fails, Rocket forwards the request to the next matching route or calls an error handler.

  3. Processing

    The request handler associated with the route is invoked with validated arguments. This is the main business logic of the application. Processing completes by returning a Response.

  4. Response

    The returned Response is processed. Rocket generates the appropriate HTTP response and sends it to the client. This completes the lifecycle. Rocket continues listening for requests, restarting the lifecycle for each incoming request.

The remainder of this section details the routing phase as well as additional components needed for Rocket to begin dispatching requests to request handlers. The sections following describe the request and response phases.

Routing#

Rocket applications are centered around routes and handlers.

A handler is simply a function that takes an arbitrary number of arguments and returns any arbitrary type. A route is a combination of:

The parameters to match against include static paths, dynamic paths, path segments, forms, query strings, request format specifiers, and body data. Rocket uses attributes, which look like function decorators in other languages, to make declaring routes easy. Routes are declared by annotating a function, the handler, with the set of parameters to match against. A complete route declaration looks like this:

1
2
3
4
#[get("/world")]              // <- route attribute
fn world() -> &'static str {  // <- request handler
    "Hello, world!"
}

This declares the world route to match against the static path "/world" on incoming GET requests. The world route is simple, but additional route parameters are necessary when building more interesting applications. The Requests section describes the available options for constructing routes.

Mounting#

Before Rocket can dispatch requests to a route, the route needs to be mounted. Mounting a route is like namespacing it. Routes are mounted via the mount method on a Rocket instance. Rocket instances can be created with the ignite() static method.

The mount method takes 1) a path to namespace a list of routes under, and 2) a list of route handlers through the routes! macro. The routes! macro ties Rocket’s code generation to your application.

For instance, to mount the world route we declared above, we would use the following code:

1
rocket::ignite().mount("/hello", routes![world])

Altogether, this creates a new Rocket instance via the ignite function and mounts the world route to the "/hello" path. As a result, GET requests to the "/hello/world" path will be directed to the world function.

Namespacing#

When a route is declared inside a module other than the root, you may find yourself with unexpected errors when mounting:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
mod other {
    #[get("/world")]
    pub fn world() -> &'static str {
        "Hello, world!"
    }
}

use other::world;

fn main() {
  // error[E0425]: cannot find value `static_rocket_route_info_for_world` in this scope
  rocket::ignite().mount("/hello", routes![world])
}

This occurs because the routes! macro implicitly converts the route’s name into the name of a structure generated by Rocket’s code generation. The solution is to name the route by a module path instead:

1
rocket::ignite().mount("/hello", routes![other::world])

Launching#

Now that Rocket knows about the route, you can tell Rocket to start accepting requests via the launch method. The method starts up the server and waits for incoming requests. When a request arrives, Rocket finds the matching route and dispatches the request to the route’s handler.

We typically call launch from the main function. Our complete Hello, world! application thus looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/world")]
fn world() -> &'static str {
    "Hello, world!"
}

fn main() {
    rocket::ignite().mount("/hello", routes![world]).launch();
}

Note that we’ve added the #![feature(plugin)] and #![plugin(rocket_codegen)] lines to tell Rust that we’ll be using Rocket’s code generation plugin. We’ve also imported the rocket crate into our namespace via extern crate rocket. Finally, we call the launch method in the main function.

Running the application, the console shows:

1
2
3
4
5
6
7
8
🔧  Configured for development.
    => address: localhost
    => port: 8000
    => log: normal
    => workers: {logical cores}
🛰  Mounting '/world':
    => GET /hello/world
🚀  Rocket has launched from http://localhost:8000...

If we visit localhost:8000/hello/world, we see Hello, world!, exactly as we expected.

A version of this example’s complete crate, ready to cargo run, can be found on GitHub. You can find dozens of other complete examples, spanning all of Rocket’s features, in the GitHub examples directory.

Configuration#

At any point in time, a Rocket application is operating in a given configuration environment. There are three such environments:

Without any action, Rocket applications run in the development environment. The environment can be changed via the ROCKET_ENV environment variable. For example, to launch the Hello, world! application in the staging environment, we can run:

1
ROCKET_ENV=stage cargo run

You’ll likely need sudo for the command to succeed since staging defaults to listening on port 80. Note that you can use the short or long form of the environment name to specify the environment, stage or staging here. Rocket tells us the environment we have chosen and its configuration when it launches:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ sudo ROCKET_ENV=staging cargo run

🔧  Configured for staging.
    => address: 0.0.0.0
    => port: 80
    => log: normal
    => workers: {logical cores}
🛰  Mounting '/':
    => GET /
🚀  Rocket has launched from http://0.0.0.0:80...

Configuration settings can be changed in one of two ways: via the Rocket.toml configuration file, or via environment variables.

Configuration File#

A Rocket.toml file can be used to specify the configuration parameters for each environment. The file is optional. If it is not present, the default configuration parameters are used.

The file must be a series of TOML tables, at most one for each environment and an optional “global” table, where each table contains key-value pairs corresponding to configuration parameters for that environment. If a configuration parameter is missing, the default value is used. The following is a complete Rocket.toml file, where every standard configuration parameter is specified with the default value:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[development]
address = "localhost"
port = 8000
workers = max(number_of_cpus, 2)
log = "normal"

[staging]
address = "0.0.0.0"
port = 80
workers = max(number_of_cpus, 2)
log = "normal"

[production]
address = "0.0.0.0"
port = 80
workers = max(number_of_cpus, 2)
log = "critical"

The workers parameter is computed by Rocket automatically; the value above is not valid TOML syntax.

The “global” pseudo-environment can be used to set and/or override configuration parameters globally. A parameter defined in a [global] table sets, or overrides if already present, that parameter in every environment. For example, given the following Rocket.toml file, the value of address will be "1.2.3.4" in every environment:

1
2
3
4
5
6
7
8
[global]
address = "1.2.3.4"

[development]
address = "localhost"

[production]
address = "0.0.0.0"

Extras#

In addition to overriding default configuration parameters, a configuration file can also define values for any number of extra configuration parameters. While these parameters aren’t used by Rocket directly, other libraries, or your own application, can use them as they wish. As an example, the Template type accepts a value for the template_dir configuration parameter. The parameter can be set in Rocket.toml as follows:

1
2
3
4
5
[development]
template_dir = "dev_templates/"

[production]
template_dir = "prod_templates/"

This sets the template_dir extra configuration parameter to "dev_templates/" when operating in the development environment and "prod_templates/" when operating in the production environment. Rocket will prepend the [extra] tag to extra configuration parameters when launching:

1
2
3
🔧  Configured for development.
    => ...
    => [extra] template_dir: "dev_templates/"

Environment Variables#

All configuration parameters, including extras, can be overridden through environment variables. To override the configuration parameter {param}, use an environment variable named ROCKET_{PARAM}. For instance, to override the “port” configuration parameter, you can run your application with:

1
2
3
4
5
ROCKET_PORT=3721 cargo run

🔧  Configured for staging.
    => ...
    => port: 3721

Environment variables take precedence over all other configuration methods: if a variable is set, it will be used as that parameter’s value.