← Table of contents

6.4 What Arrives

An address carries more than a direction. “Third floor, apartment twelve” is a route and at the same time data about who is needed. A good messenger does not ask at the door who they came for - they read it on the way. The handler was receiving the fact of arrival. What was written on the envelope - it never looked.

What You Need

Two handlers receive a task identifier in the path: GET /tasks/{id} and PATCH /tasks/{id}/done. Right now they cannot see it - both respond the same way for any number.

A way is needed to tell the handler: this request carries an {id}, and it is yours.

The Build

axum passes values from the route to the handler through function arguments: it reads the type of each argument, takes what is needed from the request, and delivers it ready-made.

Path<u64> is the argument type for a path parameter. axum knows the route contains {id}, extracts it, and converts it to u64. Path(id): Path<u64> is destructuring: Path wraps the value, Path(id) unwraps it and binds the number to the name id.

Update crates/api/src/routes.rs:

// crates/api/src/routes.rs - CHANGED
use axum::{
    Router,
    extract::Path,
    routing::{get, patch, post},
};

async fn add() -> String {
    "TODO: add task".to_string()
}

async fn list() -> String {
    "TODO: list tasks".to_string()
}

async fn get_task(Path(id): Path<u64>) -> String {
    format!("task {id}")
}

async fn done(Path(id): Path<u64>) -> String {
    format!("marking {id} done")
}

pub fn router() -> Router {
    Router::new()
        .route("/tasks", post(add).get(list))
        .route("/tasks/{id}", get(get_task))
        .route("/tasks/{id}/done", patch(done))
}

add and list receive no arguments: their routes carry no variable parts. crates/api/src/main.rs does not change.

The Result

$ make serve

In another terminal:

$ curl localhost:3000/tasks/1
task 1

$ curl localhost:3000/tasks/42
task 42

$ curl -X PATCH localhost:3000/tasks/7/done
marking 7 done

The handlers see the number. There is no data behind it yet. But the letter is now read, not just the envelope.

make ci passes: the same 38 tests.

The complete tq code for this chapter is in 6-the-port/04-what-arrives/.


Lore: How axum Fills Arguments

The arguments that axum fills from the request are called extractors. Path<T> is one of them. The mechanism is the same for all: axum reads the argument type, looks for an implementation of the FromRequestParts or FromRequest trait, and calls it. If extraction fails - for example, if {id} is not a number - axum responds with an error itself, without calling the handler.