Rust y SQLx en 2026: Consultas Verificadas en Tiempo de Compilación y Preguntas de Entrevista

Guía completa sobre SQLx con Rust: consultas SQL verificadas en compilación, comparación con ORMs, y preguntas técnicas para entrevistas de trabajo.

Rust y SQLx en 2026: Consultas Verificadas en Tiempo de Compilación y Preguntas de Entrevista

SQLx representa un enfoque revolucionario para la interacción con bases de datos en Rust. A diferencia de los ORM tradicionales, SQLx verifica las consultas SQL directamente durante la compilación, eliminando toda una categoría de errores antes de que el programa se ejecute. Esta característica única lo convierte en una herramienta preferida para aplicaciones críticas donde la confiabilidad de la capa de datos es primordial.

En 2026, SQLx se ha establecido como la solución de referencia para desarrolladores Rust que trabajan con PostgreSQL, MySQL y SQLite. Su creciente adopción en la industria significa que las preguntas relacionadas con SQLx aparecen cada vez más frecuentemente en entrevistas técnicas para posiciones de Rust.

SQLx utiliza las macros procedurales de Rust para analizar consultas SQL en tiempo de compilación. Esta verificación requiere una conexión a la base de datos durante el build, pero garantiza que cada consulta sea sintácticamente correcta y compatible con el esquema.

Configuración Inicial de SQLx

La instalación de SQLx en un proyecto Rust comienza agregando las dependencias apropiadas. La configuración varía según el motor de base de datos objetivo.

toml
[dependencies]
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "macros", "chrono", "uuid"] }
tokio = { version = "1", features = ["full"] }
dotenvy = "0.15"

La variable de entorno DATABASE_URL debe configurarse para permitir la verificación en tiempo de compilación:

bash
export DATABASE_URL="postgres://user:password@localhost:5432/mydb"

Consultas con Verificación en Tiempo de Compilación

La macro sqlx::query! constituye el núcleo de la funcionalidad de verificación. Analiza la consulta SQL, verifica su validez contra el esquema de la base de datos y genera código Rust tipado.

rust
use sqlx::PgPool;

#[derive(Debug)]
struct User {
    id: i32,
    email: String,
    created_at: chrono::DateTime<chrono::Utc>,
}

async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<Option<User>, sqlx::Error> {
    let row = sqlx::query!(
        r#"
        SELECT id, email, created_at
        FROM users
        WHERE id = $1
        "#,
        user_id
    )
    .fetch_optional(pool)
    .await?;

    Ok(row.map(|r| User {
        id: r.id,
        email: r.email,
        created_at: r.created_at,
    }))
}

Si una columna no existe o el tipo no coincide, el compilador señala el error inmediatamente.

Uso de query_as para Mapeo Automático

Para simplificar el mapeo entre resultados SQL y estructuras Rust, query_as! ofrece una sintaxis más concisa:

rust
use sqlx::FromRow;

#[derive(Debug, FromRow)]
struct Article {
    id: i32,
    title: String,
    content: String,
    author_id: i32,
    published: bool,
}

async fn get_published_articles(pool: &PgPool) -> Result<Vec<Article>, sqlx::Error> {
    sqlx::query_as!(
        Article,
        r#"
        SELECT id, title, content, author_id, published
        FROM articles
        WHERE published = true
        ORDER BY id DESC
        LIMIT 50
        "#
    )
    .fetch_all(pool)
    .await
}

Gestión de Transacciones

Las transacciones garantizan la integridad de operaciones múltiples. SQLx propone una API elegante para manipularlas:

rust
async fn transfer_funds(
    pool: &PgPool,
    from_account: i32,
    to_account: i32,
    amount: rust_decimal::Decimal,
) -> Result<(), sqlx::Error> {
    let mut tx = pool.begin().await?;

    sqlx::query!(
        "UPDATE accounts SET balance = balance - $1 WHERE id = $2",
        amount,
        from_account
    )
    .execute(&mut *tx)
    .await?;

    sqlx::query!(
        "UPDATE accounts SET balance = balance + $1 WHERE id = $2",
        amount,
        to_account
    )
    .execute(&mut *tx)
    .await?;

    tx.commit().await?;
    Ok(())
}

Modo Offline para Entornos CI/CD

En pipelines de build donde la base de datos no es accesible, SQLx soporta un modo offline. Los metadatos de las consultas se guardan en un archivo JSON:

bash
cargo sqlx prepare --workspace

Este comando genera un directorio .sqlx/ conteniendo la información de esquema necesaria para la compilación sin conexión activa.

rust
// Con SQLX_OFFLINE=true, la compilación usa los metadatos cacheados
let users = sqlx::query_as!(User, "SELECT * FROM users WHERE active = true")
    .fetch_all(&pool)
    .await?;

Comparación con Diesel y SeaORM

SQLx difiere fundamentalmente de ORMs como Diesel o SeaORM en su enfoque:

rust
// SQLx: SQL crudo con verificación
sqlx::query!("SELECT * FROM users WHERE email LIKE $1", pattern)
    .fetch_all(&pool)
    .await?;

// Diesel: DSL Rust compilado a SQL
users::table
    .filter(users::email.like(pattern))
    .load::<User>(&mut conn)?;

// SeaORM: API fluida con modelos generados
User::find()
    .filter(user::Column::Email.like(&pattern))
    .all(&db)
    .await?;

SQLx privilegia el SQL nativo, ofreciendo control total sobre las consultas mientras mantiene la seguridad de tipos. Los ORMs abstraen el SQL pero pueden limitar el acceso a características específicas de cada motor.

Gestión de Migraciones

SQLx integra un sistema de migración robusto:

bash
sqlx migrate add create_users_table

Este comando crea un archivo de migración versionado:

sql
-- migrations/20260912120000_create_users_table.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash TEXT NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);

La aplicación de migraciones se realiza programáticamente o vía CLI:

rust
sqlx::migrate!().run(&pool).await?;

Pool de Conexiones y Configuración

La configuración del pool de conexiones impacta directamente el rendimiento:

rust
use sqlx::postgres::PgPoolOptions;
use std::time::Duration;

async fn create_pool() -> Result<PgPool, sqlx::Error> {
    PgPoolOptions::new()
        .max_connections(20)
        .min_connections(5)
        .acquire_timeout(Duration::from_secs(3))
        .idle_timeout(Duration::from_secs(600))
        .max_lifetime(Duration::from_secs(1800))
        .connect(&std::env::var("DATABASE_URL").unwrap())
        .await
}

Preguntas Técnicas de Entrevista

Las entrevistas técnicas sobre SQLx exploran varios dominios clave.

Pregunta: ¿Cómo verifica SQLx las consultas en tiempo de compilación?

SQLx usa macros procedurales para establecer una conexión a la base de datos durante la compilación. La macro query! ejecuta un comando PREPARE para validar la sintaxis y los tipos, luego genera código Rust tipado basado en el esquema real.

Pregunta: ¿Cuál es la diferencia entre query! y query_as!?

La macro query! retorna un tipo anónimo con campos correspondientes a las columnas seleccionadas. query_as! mapea directamente los resultados a una estructura definida, simplificando el código cuando la estructura corresponde exactamente al resultado.

Pregunta: ¿Cómo manejar consultas dinámicas con SQLx?

rust
use sqlx::QueryBuilder;

async fn search_users(
    pool: &PgPool,
    name: Option<&str>,
    email: Option<&str>,
) -> Result<Vec<User>, sqlx::Error> {
    let mut builder: QueryBuilder<sqlx::Postgres> = QueryBuilder::new(
        "SELECT id, email, created_at FROM users WHERE 1=1"
    );

    if let Some(n) = name {
        builder.push(" AND name ILIKE ");
        builder.push_bind(format!("%{}%", n));
    }

    if let Some(e) = email {
        builder.push(" AND email = ");
        builder.push_bind(e);
    }

    builder
        .build_query_as::<User>()
        .fetch_all(pool)
        .await
}

Pregunta: ¿Cómo testear código que usa SQLx?

El enfoque recomendado usa transacciones revertidas para aislar los tests:

rust
#[sqlx::test]
async fn test_create_user(pool: PgPool) -> sqlx::Result<()> {
    let user = create_user(&pool, "test@example.com").await?;
    assert_eq!(user.email, "test@example.com");
    Ok(())
    // La transacción se revierte automáticamente
}

¿Listo para aprobar tus entrevistas de Rust?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Buenas Prácticas de Producción

Varios patrones optimizan el uso de SQLx en producción:

rust
// Wrapper para gestión centralizada de errores
pub struct Database {
    pool: PgPool,
}

impl Database {
    pub async fn new() -> Result<Self, sqlx::Error> {
        let pool = PgPoolOptions::new()
            .max_connections(20)
            .connect(&std::env::var("DATABASE_URL").unwrap())
            .await?;
        
        sqlx::migrate!().run(&pool).await?;
        
        Ok(Self { pool })
    }

    pub fn pool(&self) -> &PgPool {
        &self.pool
    }
}

La encapsulación del pool en una estructura permite agregar fácilmente funcionalidades como logging, métricas o reintentos.

Conclusión

SQLx establece un nuevo estándar para la interacción con bases de datos en Rust. Su verificación en tiempo de compilación elimina los errores SQL en tiempo de ejecución mientras preserva la flexibilidad del SQL nativo. Para desarrolladores preparando entrevistas técnicas, el dominio de SQLx demuestra una comprensión profunda del ecosistema Rust y los principios de seguridad de tipos aplicados a las capas de datos. La combinación de rendimiento óptimo, tipado fuerte y una API ergonómica hace de SQLx una elección acertada para aplicaciones Rust modernas.

Reto diario

¿Sabrías detectar el bug en Rust?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 12 de septiembre de 2026

Compartir

Artículos relacionados