Getting Started

This guide assumes you have a basic knowledge of rust. No knowledge of thruster or the web in general is necessary!

Project Setup

Create a new binary rust project using cargo:

mkdir carrier-pigeon
cd carrier-pigeon
cargo init --bin

Open up the newly generated Cargo.toml and add some dependencies for thruster, as well as it's preferred async runtime, tokio.

[package]
name = "carrier-pigeon" # Get it? Because carrier pigeons carried secrets? eh?
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# ADD THIS PART
env_logger = "0.7.1"
log = "0.4"
thruster = { version = "1.3.0", features = ["hyper_server"] }
tokio = { version = "1.6", features = ["rt", "rt-multi-thread", "macros"] }

Note that version 1.3.0 is not yet released, but will be in the near future. If you're using this guide before it has been released, then replace the version = "1.3.0" with git = "https://github.com/thruster-rs/thruster".

Here we've added thruster itself (along with a backend server, which we won't cover here) and tokio which is an async runtime for rust that allows thruster to run lots of things efficiently.

Now let's make a super-simple app to make sure everything is working. In src/main.rs, change the contents to look like this:

// 1
use log::info;
use thruster::{
    context::typed_hyper_context::TypedHyperContext,
    hyper_server::HyperServer,
    m,
    middleware_fn, App, HyperRequest, MiddlewareNext, MiddlewareResult, ThrusterServer,
};

// 2
type Ctx = TypedHyperContext<RequestConfig>;

// 3
#[derive(Default)]
struct ServerConfig {}

#[derive(Default)]
struct RequestConfig {}

// 4
fn generate_context(request: HyperRequest, _state: &ServerConfig, _path: &str) -> Ctx {
    Ctx::new(
        request,
        RequestConfig {},
    )
}

// 5
#[middleware_fn]
async fn hello(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    context.body("Hello, world!");

    Ok(context)
}

#[tokio::main]
async fn main() {
    env_logger::init();
    info!("Starting server...");

    // 6
    let app = App::<HyperRequest, Ctx, ServerConfig>::create(
        generate_context,
        ServerConfig {},
    )
    // 7
    .get("/hello", m![hello]);

    // 8
    let server = HyperServer::new(app);
    server.build("0.0.0.0", 4321).await;
}

Let's break down what we have here (the comments align with the points):

  1. All the imports for thruster, not super important but worth noting that we're using a TypedHyperContext here. I'll explain why in a few points.

  2. It's often convenient to just alias our context type, because we use it a LOT (literally every middleware function.)

  3. ServerConfig and RequestConfig aren't important now, but will be super important later. ServerConfig will basically hold the shared state of the server, while RequestConfig will hold all of the request-specific pieces that you define. Authentication results are commonly stored in the RequestConfig.

  4. This function is the backbone of thruster. It's important that this function be nice and efficient because of that. The generate_context function takes a request and turns it into a Context that's then passed through all of the handling middleware. It also has references to the shared context and path of the request just in case it's useful for the context object. Commonly, this is how references to a database pool are passed to middleware functions.

  5. A simple middleware function that sets the body of the response to "Hello, world!". Note that this function doesn't use the next function. This means that even if there's other middleware after it, it won't be called. That's what next is -- a convenient wrapper to call the "next" function in the chain of middleware.

  6. This is how we create an app. We have to tell the app what the server config looks like, and how to generate a context.

  7. This is bread and butter of thruster. It's middleware chains! the get represents the relevant verb, and the m! macro is all of the middleware to call when the route requested. m! takes a comma separated list of functions.

  8. Thruster is a few pieces, but its router is detached from the actual TCP handling. This means that we can use super optimized or highly featured libraries like Hyper or Actix to do heavy lifting for us! This example will use hyper, so we make a HyperServer and pass that app to it to serve.

Now run the program!

cargo run

If you check out http://localhost:4321/hello you should see our "Hello, world!" message -- nice!

Last updated