← Índice

6.3 Los cuatro verbos

Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.

Un artesano que solo sabe responder no es todavía un artesano - es una persona que está en casa. Existe un nombre, existe un lugar; el oficio aún no se ha anunciado. Un visitante con asuntos - añadir, buscar, marcar - se irá con las manos vacías. No porque le hayan negado la entrada. Porque no hubo acuerdo.

Lo que necesitas

Una API HTTP es un contrato: el cliente nombra un verbo y una ruta, el servidor responde. Las cuatro operaciones de tq reciben cuatro direcciones:

RutaVerboOperación
/tasksPOSTañadir una tarea
/tasksGETlistar todas las tareas
/tasks/{id}GETobtener una tarea
/tasks/{id}/donePATCHcompletar una tarea

Los verbos no se eligen de forma arbitraria - cada uno lleva un significado: GET lee sin cambiar el estado; POST crea; PATCH actualiza parte de un recurso. Más en la sección Lore de este capítulo.

Los handlers serán stubs por ahora: cadenas en lugar de datos. Paso a paso recibirán contenido real.

La construcción

Las rutas viven en un archivo separado - así crates/api/src/main.rs se ocupa solo de arrancar el servidor, y las rutas se pueden leer y cambiar de forma independiente.

Crea crates/api/src/routes.rs:

// crates/api/src/routes.rs - NUEVO
use axum::{
    Router,
    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() -> String {
    "TODO: get task".to_string()
}

async fn done() -> String {
    "TODO: mark done".to_string()
}

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

.route("/tasks", post(add).get(list)) - dos acciones en una misma ruta: POST /tasks crea, GET /tasks devuelve la lista.

{id} es un parámetro de ruta: axum extraerá el número de la URL; los stubs todavía no lo usan.

Actualiza crates/api/src/main.rs. health se queda aquí - es una ruta del servidor, no una ruta de recurso: no trata de tareas, sino de si el proceso está vivo:

// crates/api/src/main.rs - CAMBIADO
mod routes;

use axum::{Router, routing::get};

async fn health() -> String {
    "tq ok".to_string()
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(health))
        .merge(routes::router());
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

.merge(routes::router()) añade todas las rutas de routes.rs a app.

El resultado

$ make serve

En otro terminal:

$ curl -X POST localhost:3000/tasks
TODO: add task

$ curl localhost:3000/tasks
TODO: list tasks

$ curl localhost:3000/tasks/1
TODO: get task

$ curl -X PATCH localhost:3000/tasks/1/done
TODO: mark done

Cuatro rutas responden. Sin datos todavía - la forma de la API está lista.

make ci pasa: los mismos 38 tests.

El código completo de tq para este capítulo está en 6-the-port/03-the-four-verbs/.


Lore: Verbos HTTP

La convención REST asigna un significado a cada verbo:

Seguir estas convenciones no es obligatorio. Pero un cliente que conoce REST también sabe qué esperar de GET y POST sin documentación. La convención funciona en ambas direcciones.