0Pricing
Learn Rust Coding · Lección

Integración con la base de datos

Persistir datos

Integración con la base de datos es una lección gratuita de Learn Rust Coding en CoddyKit. Esta es la lección 3 de 4. 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 4 lecciones en total.

Persistir datos

Hasta ahora, nuestra API almacenaba los datos en memoria, por lo que desaparecían al reiniciar. Los servicios reales necesitan una base de datos. En esta lección conectaremos una API de Axum a PostgreSQL mediante sqlx, un conjunto de herramientas SQL asíncronas para Rust con comprobación en tiempo de compilación.

Aprenderá sobre los pools de conexiones, las consultas, el mapeo de filas a estructuras y el uso del pool como estado compartido.

Añadir sqlx

Añada sqlx con las características que necesite: un runtime, TLS y un controlador de base de datos. A continuación se muestra una configuración de Postgres con Tokio.

// Cargo.toml
// [dependencies]
// sqlx = { version = "0.7", features = [
//   "runtime-tokio", "tls-rustls", "postgres", "macros"
// ] }

El pool de conexiones

Abrir una conexión nueva por solicitud es lento. Un pool de conexiones mantiene un conjunto de conexiones reutilizables. PgPoolOptions crea uno a partir de una URL de base de datos. El pool se puede clonar fácilmente (internamente utiliza un contador de referencias), por lo que resulta ideal como estado compartido.

use sqlx::postgres::PgPoolOptions;

async fn make_pool(url: &str) -> sqlx::PgPool {
    PgPoolOptions::new()
        .max_connections(5)
        .connect(url)
        .await
        .expect("failed to connect")
}

El pool como estado de la aplicación

Pase el pool a Axum con .with_state(pool). Después, los controladores reciben State(pool): State<PgPool>. Como el pool se clona fácilmente, todas las solicitudes comparten las mismas conexiones subyacentes.

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

fn build(pool: PgPool) -> Router {
    Router::new()
        .route("/todos", get(list_todos))
        .with_state(pool)
}

Ejecutar una consulta

La función sqlx::query ejecuta SQL sin procesar. Vincule los parámetros con .bind(value) para evitar la inyección SQL; Postgres utiliza los marcadores de posición $1, $2. Use .execute(&pool) para las escrituras que no devuelven filas.

use sqlx::PgPool;

async fn insert_todo(pool: &PgPool, title: &str) -> Result<(), sqlx::Error> {
    sqlx::query("INSERT INTO todos (title, done) VALUES ($1, $2)")
        .bind(title)
        .bind(false)
        .execute(pool)
        .await?;
    Ok(())
}

Mapear filas a estructuras

Derive sqlx::FromRow en su modelo para que los resultados de las consultas se mapeen directamente en él. Use query_as::<_, Todo> con fetch_all para obtener un Vec<Todo>, o fetch_one para obtener una sola fila.

use sqlx::{PgPool, FromRow};

#[derive(FromRow, serde::Serialize)]
struct Todo { id: i32, title: String, done: bool }

async fn all_todos(pool: &PgPool) -> Result<Vec<Todo>, sqlx::Error> {
    let rows = sqlx::query_as::<_, Todo>("SELECT id, title, done FROM todos")
        .fetch_all(pool)
        .await?;
    Ok(rows)
}

Un controlador que lee la base de datos

Combine las piezas: un controlador obtiene el pool del estado, ejecuta una consulta y devuelve JSON. Asigne los errores de la base de datos al estado 500 para que el cliente reciba una respuesta limpia en lugar de un panic.

use axum::{extract::State, Json, http::StatusCode};
use sqlx::PgPool;

async fn list_todos(
    State(pool): State<PgPool>,
) -> Result<Json<Vec<Todo>>, StatusCode> {
    match all_todos(&pool).await {
        Ok(todos) => Ok(Json(todos)),
        Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
    }
}

Devolver las filas insertadas

Postgres puede devolver la fila que acaba de insertar mediante RETURNING. Combínelo con query_as y fetch_one para obtener el nuevo registro, incluido su id generado, en un único viaje de ida y vuelta.

use sqlx::PgPool;

async fn create(pool: &PgPool, title: &str) -> Result<Todo, sqlx::Error> {
    let todo = sqlx::query_as::<_, Todo>(
        "INSERT INTO todos (title, done) VALUES ($1, false) \
         RETURNING id, title, done")
        .bind(title)
        .fetch_one(pool)
        .await?;
    Ok(todo)
}

Migraciones

El esquema debe existir antes de ejecutar las consultas. sqlx admite migraciones: archivos SQL en una carpeta migrations/ que se aplican en orden. Ejecútelas al iniciar con sqlx::migrate!() para que una base de datos nueva se configure automáticamente.

use sqlx::PgPool;

async fn run_migrations(pool: &PgPool) {
    sqlx::migrate!("./migrations")
        .run(pool)
        .await
        .expect("migrations failed");
}
// migrations/0001_init.sql contains the CREATE TABLE statements.

Transacciones

Cuando varias escrituras deban completarse juntas, envuélvalas en una transacción. Comience con pool.begin(), ejecute las consultas mediante el identificador de la transacción y, después, haga commit. Si la transacción se descarta sin confirmarse, sqlx revierte los cambios automáticamente y mantiene la coherencia de los datos.

use sqlx::PgPool;

async fn transfer(pool: &PgPool) -> Result<(), sqlx::Error> {
    let mut tx = pool.begin().await?;
    sqlx::query("UPDATE accounts SET balance = balance - 10 WHERE id = 1")
        .execute(&mut *tx).await?;
    sqlx::query("UPDATE accounts SET balance = balance + 10 WHERE id = 2")
        .execute(&mut *tx).await?;
    tx.commit().await?;
    Ok(())
}

Configuración y secretos

No incluya nunca las credenciales de la base de datos directamente en el código. Lea DATABASE_URL del entorno, normalmente cargándola desde un archivo .env con el crate dotenvy durante el desarrollo. En producción, la plataforma la inyecta como variable de entorno.

use std::env;

async fn connect_from_env() -> sqlx::PgPool {
    let url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    make_pool(&url).await
}

Comprobación rápida

Compruebe cuánto entiende sobre la integración de bases de datos.

Resumen

Ha integrado una base de datos:

  • Use un pool de conexiones PgPool y compártalo mediante .with_state.
  • Use query/execute para las escrituras y query_as con FromRow para las lecturas tipadas.
  • Vincule siempre los parámetros para evitar la inyección SQL.
  • RETURNING obtiene las filas insertadas; las transacciones agrupan las escrituras de forma atómica.
  • Ejecute las migraciones al iniciar y lea DATABASE_URL del entorno.

Preguntas frecuentes

¿La lección «Integración con la base de datos» es gratis?

Sí — el texto completo de «Integración con la base de datos» 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 4 lecciones en total.

¿Qué aprenderé en «Integración con la base de datos»?

Persistir datos 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 3 de 4.

¿Cuánto tiempo toma la lección «Integración con la base de datos»?

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. Configuración del proyecto
  2. Endpoints y modelos
  3. Integración con la base de datos
  4. Pruebas de la API
← Volver a Learn Rust Coding