Lär dig programmera i Rust · Lektion

Databasintegration

Beständiggör data

Lektion 3 av 413 steg

Databasintegration är en gratis lektion i Lär dig programmera i Rust på CoddyKit. Detta är lektion 3 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Lär dig programmera i Rust, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Lär dig programmera i Rust innehåller totalt 4 lektioner.

Spara data beständigt

Hittills har vårt API lagrat data i minnet, vilket försvinner vid omstart. Riktiga tjänster behöver en databas. I den här lektionen ansluter vi ett Axum-API till PostgreSQL med sqlx, en asynkron SQL-verktygslåda för Rust med kontroll vid kompilering.

Ni får lära Er om anslutningspooler, frågor, mappning av rader till structs och hur poolen används som delat tillstånd.

Lägga till sqlx

Lägg till sqlx med de funktioner Ni behöver: en runtime, TLS och en databasedrivrutin. Nedan visas en Postgres-konfiguration som använder Tokio.

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

Anslutningspoolen

Det är långsamt att öppna en ny anslutning för varje request. En anslutningspool behåller en uppsättning återanvändbara anslutningar. PgPoolOptions bygger en pool från en databas-URL. Poolen är billig att klona (den använder referensräkning internt), vilket gör den idealisk som delat tillstånd.

use sqlx::postgres::PgPoolOptions;

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

Poolen som applikationstillstånd

Skicka poolen till Axum med .with_state(pool). Handlers tar sedan emot State(pool): State<PgPool>. Eftersom poolen kan klonas billigt delar alla requests samma underliggande anslutningar.

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

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

Köra en fråga

Funktionen sqlx::query kör rå SQL. Bind parametrar med .bind(value) för att undvika SQL-injektion; Postgres använder platshållarna $1 och $2. Använd .execute(&pool) för skrivningar som inte returnerar rader.

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(())
}

Mappa rader till structs

Härled sqlx::FromRow på Er modell, så att frågeresultat mappas direkt till den. Använd query_as::<_, Todo> med fetch_all för att hämta en Vec<Todo>, eller fetch_one för en enskild rad.

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)
}

En handler som läser databasen

Kombinera delarna: en handler hämtar poolen från tillståndet, kör en fråga och returnerar JSON. Mappa databasfel till statusen 500, så att klienten får ett tydligt svar i stället för en 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),
    }
}

Returnera insatta rader

Postgres kan returnera raden som just infogades med RETURNING. Kombinera det med query_as och fetch_one för att hämta den nya posten, inklusive dess genererade id, i en enda round trip.

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)
}

Migrationer

Ditt schema måste finnas innan frågor kan köras. sqlx stöder migrationer: SQL-filer i en mapp med namnet migrations/ som tillämpas i ordning. Kör dem vid uppstart med sqlx::migrate!(), så konfigureras en ny databas automatiskt.

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.

Transaktioner

När flera skrivningar måste lyckas tillsammans omsluter Ni dem i en transaktion. Börja med pool.begin(), kör frågor mot transaktionens handle och kör sedan commit. Om Ni släpper den utan att committa rullar sqlx automatiskt tillbaka ändringarna, så att data förblir konsekventa.

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(())
}

Konfiguration och hemligheter

Hårdkoda aldrig databasuppgifter. Läs DATABASE_URL från miljön; under utveckling läses den ofta in från en .env-fil med craten dotenvy. I produktion tillhandahåller plattformen den som en miljövariabel.

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
}

Snabb kontroll

Testa Er förståelse av databasintegration.

Sammanfattning

Ni har integrerat en databas:

  • Använd en PgPool-anslutningspool och dela den via .with_state.
  • Använd query/execute för skrivningar och query_as med FromRow för typade läsningar.
  • Bind alltid parametrar för att förhindra SQL-injektion.
  • RETURNING hämtar infogade rader och transaktioner grupperar skrivningar atomiskt.
  • Kör migreringar vid uppstart och läs DATABASE_URL från miljön.
Gratis att börja

Lär dig Rust med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
39
Lektioner
144

Vanliga frågor

Är lektionen ”Databasintegration” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Lär dig programmera i Rust, inklusive ”Databasintegration”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Lär dig programmera i Rust innehåller totalt 4 lektioner.

Vad lär jag mig i ”Databasintegration”?

Beständiggör data Ni övar på Lär dig programmera i Rust med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Lär dig programmera i Rust?

Du behöver inga förkunskaper. Utbildningen i Lär dig programmera i Rust på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”Databasintegration”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Lär dig programmera i Rust-lektionen?

Ja. Varje Lär dig programmera i Rust-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Projektkonfiguration
  2. Endpoints och modeller
  3. Databasintegration
  4. Testa API:t
← Tillbaka till Lär dig programmera i Rust