Cómo insertar datos en Diesel con Rust

Resumen

Insertar datos en una base de datos con Diesel ORM en Rust requiere definir una estructura intermedia que dé formato a los registros antes de enviarlos a PostgreSQL. Si ya creaste tus tablas y modelos, este es el siguiente paso lógico para que tu blog en Rust empiece a guardar contenido real.

¿Cómo defines una estructura para insertar datos con Diesel?

Antes de mandar cualquier dato a la tabla, necesitas una struct que represente el registro nuevo. La idea es separar el modelo que lees de la base (Post) del modelo que escribes en ella (NewPost).

En el archivo models.rs importas el esquema con use super::schema::posts y declaras la nueva estructura así:

rust #[derive(Insertable)] #[table_name = "posts"] pub struct NewPost<'a> { pub title: &'a str, pub body: &'a str, pub slug: &'a str, }

Fíjate en dos cosas. La primera, el lifetime 'a: te permite usar referencias &str dentro de la estructura sin que Rust se queje sobre la duración de los datos. La segunda, no incluyes el campo id porque la base de datos lo autogenera e incrementa solo.

¿Qué hace el macro #[derive(Insertable)] en Diesel? Le dice a Diesel que esa struct puede transformarse en una sentencia INSERT válida hacia la tabla indicada en #[table_name = "posts"].

¿Por qué usar dos structs distintas para leer y escribir?

Porque los campos cambian. Cuando lees un post, necesitas el id. Cuando lo insertas, no lo tienes todavía. Separar Post de NewPost evita que tengas que pasar valores nulos o ficticios y mantiene tu código más limpio.

¿Cómo ejecutas un INSERT con Diesel en Rust?

Ya con la estructura lista, te vas a main.rs e importas NewPost desde models. También necesitas tener use self::schema::posts disponible, no solo el dsl. Este detalle suele causar errores de compilación si lo olvidas.

La instancia se crea así:

rust let new_post = NewPost { title: "Mi primer blog post", body: "Lorem ipsum", slug: "primer-post", };

Un slug siempre va en minúsculas, sin espacios ni caracteres raros, porque suele formar parte de la URL. Por eso primer-post con guion medio y no con espacio.

Luego ejecutas la inserción:

rust let post: Post = diesel::insert_into(posts::table) .values(&new_post) .get_result(&connection) .expect("La insertada falló");

Anotar el tipo con let post: Post es clave. Sin esa anotación, Diesel no puede inferir qué estructura debe devolver get_result y el compilador te lanza un error.

¿Qué hace get_result en Diesel? Ejecuta el INSERT y devuelve el registro recién creado, ya con su id autogenerado, mapeado a la struct que tú indiques.

¿Qué errores comunes vas a encontrar al insertar?

Tres errores típicos aparecen en este flujo:

  • Olvidar los dos puntos entre el nombre del parámetro y su tipo dentro de la struct.
  • No importar el schema::posts completo, solo el dsl.
  • No anotar el tipo de retorno en el let, lo que impide la inferencia.

Resolver estos tres te deja el camino libre para ver tu primer registro en consola.

¿Cómo agregas constraints como LIMIT a una query con Diesel?

Después de insertar, el select * from posts ya te trae el registro nuevo. Si insertas un segundo post cambiando el título a Mi segundo blog post y el slug a segundo, vas a ver dos registros en la respuesta.

Para traer solo uno, encadenas .limit(1) a tu query:

rust posts.limit(1).load::<Post>(&connection)

Esto equivale a escribir LIMIT 1 en SQL puro. Así vas viendo lo sencillo que resulta agregar y quitar restricciones en Diesel: cada método encadenado es una pieza más de la consulta.

Este patrón te va a servir cuando conectes un formulario web a tu ORM y los datos lleguen desde el frontend en lugar de estar hardcodeados. Como reto, agrega un tercer registro con el título y contenido que quieras y compártelo en los comentarios.