0Pricing
Learn Rust Coding · Lección

Integración con bases de datos (SQLx/Diesel)

Integre su servicio web de Rust con bases de datos utilizando ORMs o constructores de consultas populares, como SQLx o Diesel.

Integración con bases de datos (SQLx/Diesel) es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 2 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Learn Rust Coding, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Learn Rust Coding incluye 3 lecciones en total.

¿Por qué usar bases de datos en la web?

Los servicios web a menudo necesitan almacenar y recuperar datos de forma persistente. Piense en perfiles de usuario, catálogos de productos o publicaciones de blog: estos datos deben existir en algún lugar más allá de la memoria de su programa.

Las bases de datos ofrecen una forma estructurada de gestionar estos datos y garantizan que sean seguros, coherentes y accesibles. Integrar su servicio web de Rust con una base de datos es una tarea fundamental en la mayoría de las aplicaciones del mundo real.

SQLx: su aliado para bases de datos asíncronas

En Rust contamos con excelentes herramientas para interactuar con bases de datos. Dos opciones populares son SQLx y Diesel.

  • SQLx: Un crate SQL asíncrono y escrito íntegramente en Rust, con consultas verificadas en tiempo de compilación. Suele preferirse para servicios web asíncronos creados con frameworks como Tokio.
  • Diesel: Un ORM (mapeador objeto-relacional) potente que ofrece una forma más abstracta de interactuar con la base de datos y asigna directamente los structs de Rust a tablas de la base de datos. Normalmente es síncrono.

En esta lección nos centraremos en SQLx por su sólido soporte asíncrono, que encaja muy bien con el desarrollo moderno de servicios web en Rust.

Configuración de SQLx: dependencias

Antes de escribir código, debe añadir sqlx y tokio al Cargo.toml de su proyecto. tokio proporciona el runtime asíncrono que necesita sqlx.

Por ejemplo, para usar SQLite (una base de datos sencilla basada en archivos, ideal para el desarrollo local) con SQLx, su Cargo.toml podría tener este aspecto:

[dependencies] sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] } tokio = { version = "1", features = ["full"] }

También suele utilizarse una variable de entorno DATABASE_URL para configurar la cadena de conexión a la base de datos.

Conéctese a su primera base de datos

Establezcamos una conexión con una base de datos SQLite en memoria. SQLx utiliza pools de conexiones, que gestionan varias conexiones a la base de datos de forma eficiente para atender solicitudes simultáneas en su servicio web.

Pruebe a ejecutar este ejemplo:

use sqlx::SqlitePool;
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    // Use an in-memory SQLite database for simplicity
    let database_url = "sqlite::memory:"; 
    let pool = SqlitePool::connect(&database_url).await?;
    println!("Successfully connected to the database!");

    // In a real web app, 'pool' would be passed around or stored in app state.

    Ok(())
}

Estructurar datos con tablas

Antes de poder almacenar datos, debemos definir el esquema de la base de datos mediante DDL (lenguaje de definición de datos). Creemos una tabla tasks sencilla.

La macro sqlx::query! permite insertar SQL directamente en el código de Rust. ¡Incluso comprueba el SQL en tiempo de compilación!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    // Create a 'tasks' table if it doesn't exist
    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    println!("Table 'tasks' created successfully!");
    Ok(())
}

Añadir registros nuevos

Ahora que tenemos una tabla, ¡añadamos algunos datos! Usaremos la instrucción INSERT con vinculación de parámetros. Esto protege contra la inyección SQL y hace que sus consultas sean más seguras.

Observe que se utilizan marcadores de posición ? para los parámetros y que los valores se pasan a .bind().

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    // Insert a new task
    let description = "Learn SQLx";
    let completed = false;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        description,
        completed
    ).execute(&pool).await?;

    println!("Task '{}' inserted successfully!", description);
    Ok(())
}

Recuperar datos con SELECT

Para obtener datos de nuestra tabla tasks, usamos la instrucción SELECT. SQLx proporciona métodos como .fetch_all() o .fetch_one() para obtener resultados.

Puede recorrer las filas devueltas y acceder a las columnas por nombre o por índice.

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Build a web app", false
    ).execute(&pool).await?;

    // Fetch and print all tasks
    println!("\n--- All Tasks ---");
    let rows = query!("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for row in rows {
        println!("ID: {}, Desc: {}, Done: {}", 
            row.id, row.description, row.completed);
    }
    Ok(())
}

Datos con seguridad de tipos mediante structs

Extraer datos manualmente de las filas puede provocar errores. La macro query_as! de SQLx, combinada con #[derive(FromRow)], permite asignar directamente los resultados de las consultas a structs de Rust.

Esto proporciona una sólida seguridad de tipos y hace que el código sea mucho más limpio y fácil de usar.

use sqlx::{FromRow, SqlitePool, query, query_as};
use tokio;

#[derive(Debug, FromRow)]
struct Task {
    id: i64,
    description: String,
    completed: bool,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;

    // Fetch tasks directly into our Task struct
    println!("\n--- Tasks as Structs ---");
    let tasks = query_as::<_, Task>("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for task in tasks {
        println!("{:?}", task);
    }
    Ok(())
}

Modificar y eliminar registros

Actualizar y eliminar datos sigue un patrón similar al de insertar. Se utilizan las instrucciones SQL UPDATE y DELETE con vinculación de parámetros.

  • UPDATE: Modifica las filas existentes según una cláusula WHERE.
  • DELETE: Elimina filas según una cláusula WHERE.

Tenga siempre cuidado con las cláusulas WHERE en UPDATE y DELETE para evitar pérdidas de datos involuntarias.

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Finish lesson", false
    ).execute(&pool).await?;
    let task_id_to_update = 1;

    // Update a task
    query!(
        "UPDATE tasks SET completed = ? WHERE id = ?",
        true,
        task_id_to_update
    ).execute(&pool).await?;
    println!("Task {} updated.", task_id_to_update);

    // Delete a task (uncomment to run, careful with IDs after update)
    // let task_id_to_delete = 1;
    // query!(
    //     "DELETE FROM tasks WHERE id = ?",
    //     task_id_to_delete
    // ).execute(&pool).await?;
    // println!("Task {} deleted.", task_id_to_delete);

    Ok(())
}

Ponga a prueba sus conocimientos sobre bases de datos

¿Cuáles de las siguientes afirmaciones sobre SQLx en servicios web de Rust son VERDADERAS?

Integración de bases de datos: conclusiones clave

¡Ha dado un paso importante en la creación de servicios web robustos en Rust al aprender a integrar bases de datos!

  • Persistencia: Las bases de datos son fundamentales para almacenar los datos de una aplicación.
  • SQLx: Un crate SQL asíncrono y verificado en tiempo de compilación, ideal para servicios web.
  • Pools de conexiones: Son esenciales para acceder a la base de datos de forma eficiente y simultánea.
  • CRUD: Hemos visto cómo crear (INSERT), recuperar (SELECT), actualizar (UPDATE) y eliminar (DELETE) datos.
  • Seguridad de tipos: query_as! y #[derive(FromRow)] permiten asignar datos de forma segura y ergonómica.

A continuación, puede explorar cómo integrar estas operaciones de base de datos en rutas web reales.

Preguntas frecuentes

¿La lección «Integración con bases de datos (SQLx/Diesel)» es gratis?

Sí — el texto completo de «Integración con bases de datos (SQLx/Diesel)» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Learn Rust Coding, actualiza a CoddyKit PRO. El curso de Learn Rust Coding incluye 3 lecciones en total.

¿Qué aprenderé en «Integración con bases de datos (SQLx/Diesel)»?

Integre su servicio web de Rust con bases de datos utilizando ORMs o constructores de consultas populares, como SQLx o Diesel. Practicas Learn Rust Coding con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Learn Rust Coding?

No se requiere experiencia previa. Learn Rust Coding en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 3.

¿Cuánto tiempo toma la lección «Integración con bases de datos (SQLx/Diesel)»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Learn Rust Coding?

Sí. Cada lección de Learn Rust Coding incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. API REST con Actix-web/Rocket
  2. Integración con bases de datos (SQLx/Diesel)
  3. Autenticación y autorización
← Volver a Learn Rust Coding