Skip to content

Routing

Route definition

Routes, like all Pavex components, are defined using attributes.

app/src/routes/ping.rs
use pavex::get;
use pavex::http::StatusCode;

/// Respond with a `200 OK` status code to indicate that the server is alive
/// and ready to accept new requests.
#[get(path = "/ping")] // (1)!
pub fn ping() -> StatusCode {
    StatusCode::OK
}
  1. #[pavex::get] defines a route that responds to GET requests for the specified path.

Route registration

It's not enough to define a route, you also need to register it with the Blueprint. That's taken care of by the .routes() invocation:

app/src/blueprint.rs
// [...]
use pavex::{Blueprint, blueprint::from};

/// The main blueprint, defining all the components used in this API.
pub fn blueprint() -> Blueprint {
    // [...]
    bp.prefix("/api").routes(from![crate]);
    // [...]
}

It's registering, in bulk, all the routes defined in the current crate, including our ping. Since it introduces a prefix, the final route is GET /api/ping rather than GET /ping.

Add a new route

The ping function is fairly boring: it doesn't take any arguments, and it always returns the same response.

Let's spice things up with a new route: GET /api/greet/{name}. Its path includes a dynamic path parameter (name) and we want it to return a success response with Hello, {name} as its body.

Create a new greet module under routes to hold the route definition:

app/src/routes/mod.rs
pub mod greet;
pub mod ping;
app/src/routes/greet.rs
use pavex::{Response, get};

#[get(path = "/greet/{name}")] // (1)!
pub fn greet() -> Response {
    todo!()
}
  1. Dynamic path parameters are enclosed in curly braces ({}).

The body of the GET /api/greet/{name} handler is stubbed out with todo!() for now, but we'll fix that soon enough.

Extract path parameters

To access the name route parameter from your new handler you must use the PathParams extractor:

app/src/routes/greet.rs
use pavex::{Response, get, request::path::PathParams};

#[PathParams]
pub struct GreetParams {
    pub name: String, // (1)!
}

#[get(path = "/greet/{name}")]
pub fn greet(params: PathParams<GreetParams> /* (2)! */) -> Response {
    let GreetParams { name } /* (3)! */ = params.0;
    Response::ok() // (4)!
        .set_typed_body(format!("Hello, {name}!")) // (5)!
}
  1. The name of the field must match the name of the route parameter as it appears in the path we registered with the Blueprint.
  2. The PathParams extractor is generic over the type of the path parameters. In this case, we're using the GreetParams type we just defined.
  3. This is an example of Rust's destructuring syntax.
  4. Response has a convenient constructor for each HTTP status code: Response::ok starts building a Response with a 200 OK status code.
  5. set_typed_body sets the body of the response and automatically infers a suitable value for the Content-Type header based on the response body type.

Does it work? Only one way to find out! Re-launch the application and issue a new request: (1)

  1. Remember to use cargo px run instead of cargo run!
curl http://localhost:8000/api/greet/Ursula

You should see Hello, Ursula! in your terminal if everything went well.