Rust en SQLx in 2026: Compile-Time Gecontroleerde Queries en Sollicitatievragen

Een uitgebreide handleiding voor SQLx 0.9 in Rust met compile-time query-validatie, het nieuwe sqlx.toml configuratiesysteem en praktische sollicitatievragen voor Rust-ontwikkelaars.

Rust SQLx compile-time query verification tutorial

SQLx 0.9 transformeert database-toegang in Rust door SQL-fouten te detecteren tijdens compilatie in plaats van runtime. Uitgebracht in mei 2026, introduceert deze versie een nieuw configuratiesysteem, runtime-flexibiliteit en strengere query-beveiliging via de SqlSafeStr trait.

Wat SQLx Onderscheidt

In tegenstelling tot ORMs die SQL genereren uit Rust structs, verifieert SQLx handgeschreven SQL tegen een live database tijdens compilatie. Een typefout in een kolomnaam laat de build falen, niet productie.

Compile-Time Query-Verificatie in SQLx 0.9

De query! macro controleert elke SQL-statement tegen het databaseschema tijdens cargo build. Dit vangt typefouten, type-mismatches en ontbrekende kolommen op voordat de code ooit wordt uitgevoerd.

src/db/users.rsrust
use sqlx::{FromRow, PgPool};

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

pub async fn get_user_by_email(pool: &PgPool, email: &str) -> Result<Option<User>, sqlx::Error> {
    // Compile-time geverifieerd: kolomnamen, types en retourstructuur
    sqlx::query_as!(
        User,
        r#"
        SELECT id, email, created_at
        FROM users
        WHERE email = $1
        "#,
        email
    )
    .fetch_optional(pool)
    .await
}

De query_as! macro mapt rijen direct naar de User struct. Als de users tabel geen created_at kolom heeft, faalt de compilatie met een duidelijke fout die naar de exacte regel verwijst.

Het sqlx.toml Configuratiesysteem

SQLx 0.9 introduceert een sqlx.toml bestand dat databaseconfiguratie, type-overrides en multi-database setups centraliseert. Dit vervangt verspreide omgevingsvariabelen en vereenvoudigt CI-pipelines.

toml
# sqlx.toml
[common]
database_url_var = "DATABASE_URL"

[macros]
default_type_override.uuid = "uuid::Uuid"
default_type_override.timestamptz = "chrono::DateTime<chrono::Utc>"

[sqlite]
extensions = ["uuid", "crypto"]

Type-overrides elimineren repetitieve casting. Elke UUID-kolom wordt automatisch gemapt naar uuid::Uuid, en elke timestamptz naar chrono::DateTime zonder per-query annotaties.

SqlSafeStr: SQL-Injectie Voorkomen op Type-Niveau

SQLx 0.9 versterkt query-beveiliging door SqlSafeStr te vereisen voor string-parameters. Standaard implementeert alleen &'static str deze trait, waardoor dynamische strings die injectie-payloads kunnen bevatten worden geblokkeerd.

src/db/search.rsrust
use sqlx::{AssertSqlSafe, PgPool};

// Statische tabelnaam: compileert direct
const TABLE: &str = "products";

pub async fn search_products(
    pool: &PgPool,
    user_query: &str,
) -> Result<Vec<Product>, sqlx::Error> {
    // Veilig: $1 is een geparametriseerde waarde, geen geïnterpoleerde SQL
    sqlx::query_as!(
        Product,
        r#"SELECT * FROM products WHERE name ILIKE '%' || $1 || '%'"#,
        user_query
    )
    .fetch_all(pool)
    .await
}

// Dynamische tabelnaam uit config: wrap in AssertSqlSafe na validatie
pub async fn get_from_table(
    pool: &PgPool,
    table_name: &str,
) -> Result<Vec<Product>, sqlx::Error> {
    // Ontwikkelaar neemt verantwoordelijkheid voor validatie
    let safe_table = AssertSqlSafe(table_name);
    sqlx::query_as(&format!("SELECT * FROM {}", safe_table))
        .fetch_all(pool)
        .await
}

De SqlSafeStr trait dwingt ontwikkelaars om expliciet na te denken over injectierisico's. Productiecode moet dynamische tabelnamen valideren tegen een allowlist voordat AssertSqlSafe wordt gebruikt.

Runtime-Flexibiliteit met de runtime Feature

SQLx 0.9 ontkoppelt de async runtime-keuze van de crate-configuratie. De nieuwe runtime feature stelt applicaties in staat om te wisselen tussen Tokio en async-std zonder SQLx-dependencies te wijzigen.

toml
# Cargo.toml
[dependencies]
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }
tokio = { version = "1", features = ["full"] }

Voor projecten die async-std gebruiken:

toml
[dependencies]
sqlx = { version = "0.9", features = ["runtime-async-std", "postgres"] }
async-std = { version = "1", features = ["attributes"] }

Connection Pool Management en Health Checks

De connection pool in SQLx 0.9 bevat verbeterde health checks en connection lifecycle management. Pool-opties bieden fijnmazige controle over connection recycling.

src/db/pool.rsrust
use sqlx::postgres::{PgPoolOptions, PgConnectOptions};
use std::time::Duration;

pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
    let connect_options = PgConnectOptions::new()
        .host("localhost")
        .port(5432)
        .database("app_db")
        .username("app_user")
        .password("secure_password");

    PgPoolOptions::new()
        .max_connections(20)
        .min_connections(5)
        .acquire_timeout(Duration::from_secs(5))
        .idle_timeout(Duration::from_secs(600))
        .max_lifetime(Duration::from_secs(1800))
        .test_before_acquire(true)
        .connect_with(connect_options)
        .await
}

De test_before_acquire optie voert een ping uit voordat connections worden overhandigd aan applicatiecode, waardoor mislukte connections die zijn losgekoppeld van de database worden gedetecteerd.

Transacties en Savepoints

SQLx biedt type-safe transacties met automatische rollback bij fouten. Geneste transacties gebruiken savepoints voor partiële rollback-mogelijkheden.

src/db/orders.rsrust
use sqlx::PgPool;

pub async fn create_order_with_items(
    pool: &PgPool,
    user_id: i32,
    items: Vec<OrderItem>,
) -> Result<i32, sqlx::Error> {
    let mut tx = pool.begin().await?;

    // Order aanmaken
    let order_id: i32 = sqlx::query_scalar!(
        "INSERT INTO orders (user_id, status) VALUES ($1, 'pending') RETURNING id",
        user_id
    )
    .fetch_one(&mut *tx)
    .await?;

    // Savepoint voor items
    let savepoint = tx.begin().await?;
    
    for item in items {
        sqlx::query!(
            "INSERT INTO order_items (order_id, product_id, quantity) VALUES ($1, $2, $3)",
            order_id,
            item.product_id,
            item.quantity
        )
        .execute(&mut *savepoint)
        .await?;
    }
    
    savepoint.commit().await?;
    tx.commit().await?;

    Ok(order_id)
}

SQLx vs Diesel vs SeaORM: Een Vergelijking

De keuze van de juiste database-tool hangt af van projectvereisten. Hier is een vergelijking van de drie toonaangevende Rust database-bibliotheken:

EigenschapSQLxDieselSeaORM
Query-stijlRuwe SQLQuery Builder DSLActiveRecord-patroon
Compile-time controleJa (tegen live DB)Ja (tegen schema)Nee
Async ondersteuningNatiefVia diesel-asyncNatief
LeercurveLaag (SQL-kennis)Gemiddeld (DSL leren)Laag (ORM-vertrouwd)
Migratiessqlx-cliDiesel CLISeaORM CLI

SQLx is het beste voor teams met sterke SQL-expertise die maximale controle over queries nodig hebben. Diesel is geschikt voor projecten die statische schema-validatie zonder databaseverbinding tijdens de build prefereren. SeaORM biedt een vertrouwde ORM-ervaring voor ontwikkelaars die van andere talen komen.

Klaar om je Rust gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Sollicitatievragen over Rust en SQLx

De volgende vragen worden vaak gesteld in technische sollicitaties voor Rust backend-posities:

Hoe werkt de compile-time validatie van SQLx?

SQLx maakt verbinding met een echte database tijdens cargo build en valideert elke query! aanroep tegen het live schema. De compiler cachet query-metadata in een .sqlx bestand dat kan worden ingecheckt voor offline compilatie. Deze validatie controleert kolomnamen, parametertypes en retourstructuren.

Wat is het verschil tussen query! en query_as!?

De query! macro retourneert een anoniem record-type met benoemde velden toegankelijk via .field_name. De query_as! macro mapt resultaten naar een aangepaste struct die FromRow implementeert. De laatste heeft de voorkeur wanneer query-resultaten binnen een applicatie worden doorgegeven.

Hoe worden NULL-waarden behandeld in SQLx?

Kolommen die nullable zijn worden gemapt naar Option<T> in Rust. SQLx leidt nullability af uit het databaseschema. Voor queries die compile-time validatie omzeilen, kan het #[sqlx(default)] attribuut standaardwaarden bieden voor ontbrekende kolommen.

Leg Connection Pooling in SQLx uit

SQLx gebruikt een async-aware connection pool die connections hergebruikt over meerdere tasks. De pool houdt een minimum aantal idle connections aan en maakt nieuwe aan tot het geconfigureerde maximum. Connections worden gecontroleerd op gezondheid voordat ze aan de applicatie worden geleverd en gerecycled na het overschrijden van de maximale levensduur.

Hoe voorkomt SqlSafeStr SQL-injectie?

De SqlSafeStr trait markeert strings die veilig zijn voor SQL-interpolatie. Standaard implementeren alleen statische string-literalen deze trait. Dynamische strings vereisen expliciete wrapping met AssertSqlSafe, waardoor ontwikkelaars gedwongen worden om na te denken over injectierisico's en input te valideren voor gebruik.

Wanneer SQLx gebruiken in plaats van Diesel?

SQLx heeft de voorkeur wanneer complexe queries leesbare SQL vereisen, wanneer het team sterke SQL-expertise heeft, of wanneer async runtime-compatibiliteit kritisch is. Diesel is beter wanneer compile-time schema-validatie zonder databaseverbinding nodig is of wanneer een type-safe query builder API de voorkeur heeft.

Migraties met sqlx-cli

De sqlx-cli tool beheert databasemigraties en schema-synchronisatie. Migraties worden opgeslagen als SQL-bestanden met versie-prefixen.

bash
# sqlx-cli installeren
cargo install sqlx-cli --features postgres

# Nieuwe migratie aanmaken
sqlx migrate add create_users_table

# Migraties uitvoeren
sqlx migrate run

# Laatste migratie terugdraaien
sqlx migrate revert

Een migratie bestaat uit twee bestanden:

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

CREATE INDEX idx_users_email ON users(email);
sql
-- migrations/20260912000000_create_users_table.down.sql
DROP TABLE IF EXISTS users;

Offline Modus voor CI/CD

Voor CI-omgevingen zonder databasetoegang genereert SQLx metadata-bestanden die kunnen worden ingecheckt:

bash
# Query-metadata genereren
cargo sqlx prepare

# .sqlx directory committen in Git
git add .sqlx
git commit -m "Update SQLx query metadata"

De CI-pipeline kan dan compileren zonder databaseverbinding door de metadata-bestanden te gebruiken.

Conclusie

SQLx 0.9 vertegenwoordigt een significante vooruitgang voor type-safe database-interacties in Rust. De compile-time query-validatie vangt fouten vroeg op, het nieuwe configuratiesysteem vereenvoudigt multi-database setups, en de SqlSafeStr trait voegt een extra beveiligingslaag toe tegen SQL-injectie. Voor ontwikkelaars die zich voorbereiden op Rust backend-sollicitaties is het begrijpen van deze concepten essentieel, aangezien SQLx de standaardkeuze is geworden voor asynchrone databasetoegang in het Rust-ecosysteem.

Dagelijkse challenge

Zie jij de bug in Rust?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 12 september 2026

Tags

#rust
#sqlx
#database
#postgresql
#interview

Delen

Gerelateerde artikelen