Endpoint POST en Rust con Serde y Postman

Resumen

Crear un endpoint POST en Rust te permite recibir datos en formato JSON desde el navegador o un cliente como Postman y guardarlos en la base de datos. Aquí aprendes a configurar Serde, definir un handler, encapsular la lógica en una función reutilizable y probar todo con Postman para popular blog posts dinámicamente.

¿Cómo instalar Postman para probar tu API en Rust?

Postman es la herramienta que vas a usar para enviar peticiones HTTP a tu servidor sin necesidad de un frontend. Descárgalo desde su página oficial eligiendo tu sistema operativo. En Mac, basta con descargar el .zip, descomprimirlo y darle doble clic al archivo para instalar.

Una vez abierto, creas una nueva request con el botón de más, eliges el método (GET, POST, etc.) y apuntas a tu servidor local en 127.0.0.1:8000. Si tu servidor está corriendo, te responderá con los datos esperados [00:55].

¿Qué es Postman? Es un cliente HTTP que te permite enviar peticiones GET, POST, PUT y DELETE a tu API para probar endpoints sin necesidad de construir un frontend.

¿Qué dependencias necesitas en Cargo para manejar JSON en Rust?

Para que Rust pueda serializar y deserializar JSON, necesitas tres crates clave en tu archivo Cargo.toml:

  • serde con la feature derive para usar los macros automáticamente.
  • serde_json para manejar la conversión entre objetos y JSON.
  • actix-web ya lo tienes configurado como servidor.

Estos serializadores convierten tus structs en JSON y viceversa, lo que resulta útil cuando recibes datos desde el cliente [02:30].

¿Cómo preparar los modelos para JSON?

En tu archivo de modelos importas los macros con use serde::{Deserialize, Serialize}; y los agregas como derive junto a los que ya tenías como Queryable y Debug. Con esto, tus structs Post y NewPost se transforman en JSON sin esfuerzo.

¿Cómo defines un endpoint POST con Actix Web?

La estructura es muy parecida a un GET, pero usando el macro #[post("/new_post")] y una función async fn new_post. Adentro reutilizas la conexión a la base de datos del pool y el match que maneja errores con web::block.

La diferencia está en la query: en lugar de leer registros, usas diesel::insert_into(posts::table).values(&new_post).get_result::<Post>(conn) para insertar y devolver el registro creado [05:40].

No olvides registrar el servicio con .service(new_post) en tu App::new(), porque sin eso recibirás un error 404 al hacer la petición.

¿Cómo recibir datos JSON en el endpoint?

Necesitas un struct que actúe como serializador de entrada. En el módulo de modelos creas:

rust #[derive(Serialize, Deserialize, Debug, Clone)] pub struct NewPostHandler { pub title: String, pub body: String, }

Luego, en la firma del endpoint, agregas el parámetro item: web::Json<NewPostHandler> para que Actix sepa que debe esperar un JSON con esa forma. Accedes a los campos con item.title.clone() e item.body.clone() [09:15].

¿Qué hace web::Json en Actix? Es un extractor que convierte automáticamente el cuerpo JSON de la petición en una struct de Rust deserializable.

¿Cómo probar el POST desde Postman?

En Postman seleccionas el método POST, apuntas a 127.0.0.1:8000/new_post, vas a la pestaña Body, eliges raw y JSON, y escribes:

{ "title": "este es un blog en JSON", "body": "y tiene un body" }

Al darle send, recibes el post creado con su id autogenerado.

¿Cómo encapsular la lógica de inserción en una función reutilizable?

Meter toda la lógica dentro del endpoint genera problemas de lifetimes y reduce la legibilidad. La solución es implementar una función asociada al modelo Post:

rust impl Post { pub fn create_post<'a>( conn: &mut PgConnection, post: &NewPostHandler, ) -> Result<Post, diesel::result::Error> { let slug = Post::slugify(post.title.clone()); let new_post = NewPost { title: &post.title, slug: &slug, body: &post.body, }; diesel::insert_into(posts::table) .values(&new_post) .get_result::<Post>(conn) } }

¿Cómo generar slugs automáticamente desde el título?

Un slug es la versión amigable de la URL. Como NewPostHandler no incluye slug, lo generas con una función auxiliar:

rust fn slugify(title: String) -> String { title.replace(" ", "-").to_lowercase() }

Esta función reemplaza espacios por guiones y convierte todo a minúsculas, dejando un slug limpio listo para usar en la URL [13:20].

¿Qué errores comunes vas a encontrar y cómo los resuelves?

Al compilar con cargo run aparecen errores típicos que conviene reconocer:

  • Olvidar importar NewPost o NewPostHandler en el archivo del endpoint.
  • Confundir Post con NewPost al llamar a insert_into.
  • Falta de tipo explícito en get_result, que se resuelve con get_result::<Post>(conn).
  • Importar todo el prelude de Diesel con use diesel::prelude::*;.

Una vez resueltos, el servidor compila y al lanzar peticiones desde Postman ves cómo los registros aparecen en la lista al hacer GET. Si insertas un post con id 8, el siguiente GET ya lo muestra junto a los anteriores.

¿Te animas a extender este endpoint para aceptar más campos o validar el JSON antes de guardar? Cuéntame en los comentarios cómo lo implementarías.