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:
| Ruta | Verbo | Operación |
|---|---|---|
/tasks | POST | añadir una tarea |
/tasks | GET | listar todas las tareas |
/tasks/{id} | GET | obtener una tarea |
/tasks/{id}/done | PATCH | completar 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
tqpara este capítulo está en6-the-port/03-the-four-verbs/.
Lore: Verbos HTTP
La convención REST asigna un significado a cada verbo:
- GET - leer sin cambiar el estado. Dos llamadas
GET /tasksidénticas devuelven el mismo resultado. Un navegador puede cachearlas; un cliente puede reintentarlas sin riesgo. - POST - crear un nuevo recurso. Dos llamadas
POST /tasksidénticas crean dos tareas. - PATCH - actualizar parcialmente un recurso.
PATCH /tasks/{id}/donecambia solo el estado. - PUT - reemplazar un recurso por completo.
tqno lo usa: una tarea no se sobreescribe, solo se actualiza. - DELETE - eliminar. En
tqlas tareas no se eliminan - solo se completan. Si se añadiera la eliminación, seríaDELETE /tasks/{id}.
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.