Learn Rust Coding · Lektion

Datenbankintegration

Daten speichern

Lektion 3 von 413 Schritte

Datenbankintegration ist eine kostenlose Learn Rust Coding-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Learn Rust Coding-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Daten persistieren

Bisher hat unsere API Daten im Speicher abgelegt, die bei einem Neustart verloren gehen. Echte Dienste benötigen eine Datenbank. In dieser Lektion verbinden wir eine Axum-API mit PostgreSQL und verwenden sqlx, ein asynchrones, zur Compile-Zeit geprüftes SQL-Toolkit für Rust.

Sie lernen Verbindungspools, Abfragen, die Zuordnung von Zeilen zu Structs und die Verwendung des Pools als gemeinsam genutzten Zustand kennen.

sqlx hinzufügen

Fügen Sie sqlx mit den benötigten Features hinzu: einer Laufzeitumgebung, TLS und einem Datenbanktreiber. Unten sehen Sie eine Postgres-Konfiguration mit Tokio.

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

Der Verbindungspool

Für jede Anfrage eine neue Verbindung zu öffnen, ist langsam. Ein Verbindungspool hält eine Gruppe wiederverwendbarer Verbindungen bereit. PgPoolOptions erstellt einen solchen Pool aus einer Datenbank-URL. Der Pool kann kostengünstig geklont werden (intern verwendet er Referenzzählung) und eignet sich daher ideal als gemeinsam genutzter Zustand.

use sqlx::postgres::PgPoolOptions;

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

Der Pool als Anwendungszustand

Übergeben Sie den Pool mit .with_state(pool) an Axum. Handler erhalten ihn dann als State(pool): State<PgPool>. Da das Klonen des Pools kostengünstig ist, teilen sich alle Anfragen dieselben zugrunde liegenden Verbindungen.

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

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

Eine Abfrage ausführen

Die Funktion sqlx::query führt unverarbeitetes SQL aus. Binden Sie Parameter mit .bind(value), um SQL-Injection zu vermeiden; Postgres verwendet die Platzhalter $1 und $2. Verwenden Sie .execute(&pool) für Schreibvorgänge, die keine Zeilen zurückgeben.

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

Zeilen Structs zuordnen

Leiten Sie bei Ihrem Modell sqlx::FromRow ab, damit Abfrageergebnisse direkt darin abgebildet werden können. Verwenden Sie query_as::<_, Todo> mit fetch_all, um ein Vec<Todo> zu erhalten, oder fetch_one für eine einzelne Zeile.

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

Ein Handler, der die Datenbank liest

Führen Sie die Bausteine zusammen: Ein Handler erhält den Pool aus dem Zustand, führt eine Abfrage aus und gibt JSON zurück. Ordnen Sie Datenbankfehler dem Status 500 zu, damit der Client eine saubere Antwort statt eines Panics erhält.

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

Eingefügte Zeilen zurückgeben

Postgres kann mit RETURNING die gerade eingefügte Zeile zurückgeben. Kombinieren Sie dies mit query_as und fetch_one, um den neuen Datensatz einschließlich seiner generierten ID in einem einzigen Roundtrip abzurufen.

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

Migrationen

Ihr Schema muss vorhanden sein, bevor Abfragen ausgeführt werden. sqlx unterstützt Migrationen: SQL-Dateien in einem Ordner migrations/, die der Reihe nach angewendet werden. Führen Sie sie beim Start mit sqlx::migrate!() aus, damit eine leere Datenbank automatisch eingerichtet wird.

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.

Transaktionen

Wenn mehrere Schreibvorgänge gemeinsam erfolgreich sein müssen, fassen Sie sie in einer Transaktion zusammen. Beginnen Sie mit pool.begin(), führen Sie Abfragen über das Transaktions-Handle aus und rufen Sie anschließend commit auf. Wenn Sie es ohne Commit verwerfen, führt sqlx automatisch ein Rollback durch und hält die Daten konsistent.

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 und Geheimnisse

Codieren Sie Datenbankzugangsdaten niemals fest ein. Lesen Sie DATABASE_URL aus der Umgebung, die während der Entwicklung häufig mit dem Crate dotenvy aus einer .env-Datei geladen wird. In der Produktion stellt die Plattform die Variable als Umgebungsvariable bereit.

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
}

Kurze Verständnisprüfung

Testen Sie Ihr Verständnis der Datenbankintegration.

Zusammenfassung

Sie haben eine Datenbank integriert:

  • Verwenden Sie einen PgPool-Verbindungspool und teilen Sie ihn über .with_state.
  • Verwenden Sie query/execute für Schreibvorgänge und query_as mit FromRow für typisierte Lesevorgänge.
  • Binden Sie Parameter immer, um SQL-Injection zu verhindern.
  • Mit RETURNING werden eingefügte Datensätze abgerufen; Transaktionen gruppieren Schreibvorgänge atomar.
  • Führen Sie Migrationen beim Start aus und lesen Sie DATABASE_URL aus der Umgebung.
Kostenlos starten

Lerne Rust mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
39
Lektionen
144

Häufig gestellte Fragen

Ist die Lektion „Datenbankintegration“ kostenlos?

Ja — der vollständige Text von „Datenbankintegration“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Learn Rust Coding-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Datenbankintegration“?

Daten speichern Du übst Learn Rust Coding mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Learn Rust Coding zu starten?

Keine Vorkenntnisse erforderlich. Learn Rust Coding auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Datenbankintegration“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Learn Rust Coding-Lektion Code schreiben und ausführen?

Ja. Jede Learn Rust Coding-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Projekteinrichtung
  2. Endpoints und Modelle
  3. Datenbankintegration
  4. Die API testen
← Zurück zu Learn Rust Coding