Skip to content

Routing

Route registration

All the routes exposed by your API must be registered with its Blueprint. In the snippet below you can see the registration of the GET /api/ping route, the one you targeted with your curl request.

app/src/routes/mod.rs
// [...]
use pavex::blueprint::{router::GET, Blueprint};
use pavex::f;

pub fn register(bp: &mut Blueprint) {
    bp.route(GET, "/api/ping", f!(self::ping::get));
}

It specifies:

  • The HTTP method (GET)
  • The path (/api/ping)
  • An unambiguous path to the handler function (self::ping::get), wrapped in the f! macro

Request handlers

The ping function is the handler for the GET /api/ping route:

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

/// Respond with a `200 OK` status code to indicate that the server is alive
/// and ready to accept new requests.
pub fn get() -> StatusCode {
    StatusCode::OK
}

It's a public function that returns a StatusCode. StatusCode is a valid response type for a Pavex handler since it implements the IntoResponse trait: the framework knows how to convert it into a "full" Response object.

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}. It takes a dynamic route parameter (name) and we want it to return a success response with Hello, {name} as its body.

Create a new module, greet.rs, in the app/src/routes folder:

app/src/routes/mod.rs
pub mod greet;
pub mod ping;
app/src/routes/greet.rs
use pavex::response::Response;

pub fn get() -> Response {
    todo!()
}

The body of the GET /api/greet/{name} handler is stubbed out with todo!() for now, but we'll fix that soon enough. Let's register the new route with the Blueprint in the meantime:

app/src/routes/mod.rs
// [...]
pub fn register(bp: &mut Blueprint) {
    bp.route(GET, "/api/ping", f!(self::ping::get));
    bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); // (1)!
}
  1. Dynamic path parameters are prefixed with a colon (:).

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::request::path::PathParams;
use pavex::response::Response;

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

pub fn get(params: PathParams<GreetParams> /* (2)! */) -> Response {
    todo!()
}
  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.

You can now return the expected response from the handler:

app/src/routes/greet.rs
use pavex::request::path::PathParams;
use pavex::response::Response;

#[PathParams]
pub struct GreetParams {
    pub name: String,
}

pub fn get(params: PathParams<GreetParams>) -> Response {
    let GreetParams { name }/* (1)! */ = params.0;
    Response::ok() // (2)!
        .set_typed_body(format!("Hello, {name}!")) // (3)!
}
  1. This is an example of Rust's destructuring syntax.
  2. Response has a convenient constructor for each HTTP status code: Response::ok starts building a Response with a 200 OK status code.
  3. 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.