0Pricing
Learn Rust Coding · Lekcja

Integracja z bazą danych

Trwałe przechowywanie danych

Integracja z bazą danych to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.

Trwałe przechowywanie danych

Do tej pory nasze API przechowywało dane w pamięci, więc znikały one po ponownym uruchomieniu. Rzeczywiste usługi potrzebują bazy danych. W tej lekcji połączymy interfejs API Axum z PostgreSQL za pomocą sqlx — asynchronicznego narzędzia SQL dla Rust, które sprawdza zapytania podczas kompilacji.

Nauczysz się korzystać z pul połączeń, wykonywać zapytania, mapować wiersze na struktury oraz używać puli jako współdzielonego stanu.

Dodawanie sqlx

Dodaj sqlx z potrzebnymi funkcjami: środowiskiem uruchomieniowym, TLS i sterownikiem bazy danych. Poniżej przedstawiono konfigurację Postgresa korzystającą z Tokio.

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

Pula połączeń

Otwieranie nowego połączenia dla każdego żądania jest powolne. Pula połączeń przechowuje zestaw połączeń wielokrotnego użytku. PgPoolOptions tworzy ją na podstawie adresu URL bazy danych. Pula jest tania do klonowania (wewnętrznie korzysta z liczenia odwołań), dzięki czemu idealnie nadaje się na współdzielony stan.

use sqlx::postgres::PgPoolOptions;

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

Pula jako stan aplikacji

Przekaż pulę do Axum za pomocą .with_state(pool). Handlery przyjmują następnie State(pool): State<PgPool>. Ponieważ klonowanie puli jest tanie, każde żądanie współdzieli te same połączenia bazowe.

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

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

Wykonywanie zapytania

Funkcja sqlx::query wykonuje surowy SQL. Bindowanie parametrów za pomocą .bind(value) chroni przed SQL injection; Postgres używa symboli zastępczych $1, $2. Użyj .execute(&pool) dla operacji zapisu, które nie zwracają wierszy.

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

Mapowanie wierszy na struktury

Wyprowadź dla modelu implementację sqlx::FromRow, aby wyniki zapytań były bezpośrednio mapowane na ten model. Użyj query_as::<_, Todo> wraz z fetch_all, aby otrzymać Vec<Todo>, albo fetch_one dla pojedynczego wiersza.

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

Handler odczytujący bazę danych

Połącz wszystkie elementy: handler pobiera pulę ze stanu, wykonuje zapytanie i zwraca JSON. Mapuj błędy bazy danych na status 500, aby klient otrzymał poprawną odpowiedź zamiast paniki programu.

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

Zwracanie wstawionych wierszy

Postgres może zwrócić wiersz, który właśnie wstawił, za pomocą RETURNING. Połącz to z query_as i fetch_one, aby w jednym przebiegu pobrać nowy rekord, w tym wygenerowane id.

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

Migracje

Schemat musi istnieć przed wykonaniem zapytań. sqlx obsługuje migracje: pliki SQL w folderze migrations/ stosowane w kolejności. Uruchamiaj je podczas startu za pomocą sqlx::migrate!(), aby nowa baza danych była konfigurowana automatycznie.

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.

Transakcje

Gdy kilka operacji zapisu musi zakończyć się powodzeniem razem, umieść je w transakcji. Rozpocznij ją za pomocą pool.begin(), wykonuj zapytania na uchwycie transakcji, a następnie wywołaj commit. Jeśli usuniesz uchwyt bez zatwierdzenia, sqlx automatycznie wycofa transakcję, zachowując spójność danych.

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

Konfiguracja i dane uwierzytelniające

Nigdy nie umieszczaj danych uwierzytelniających do bazy na stałe w kodzie. Odczytuj DATABASE_URL ze środowiska; podczas tworzenia aplikacji jest on często ładowany z pliku .env za pomocą crate’a dotenvy. W środowisku produkcyjnym platforma wstrzykuje go jako zmienną środowiskową.

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
}

Szybkie sprawdzenie

Sprawdź swoją wiedzę o integracji z bazą danych.

Podsumowanie

Zintegrował(a) Pan(i) bazę danych:

  • Proszę używać puli połączeń PgPool i udostępniać ją za pomocą .with_state.
  • query/execute służą do zapisów, a query_as wraz z FromRow do typowanych odczytów.
  • Proszę zawsze wiązać parametry, aby zapobiegać wstrzykiwaniu SQL.
  • RETURNING pobiera wstawione wiersze, a transakcje grupują zapisy atomowo.
  • Proszę uruchamiać migracje podczas startu aplikacji i odczytywać DATABASE_URL ze zmiennych środowiskowych.

Często zadawane pytania

Czy lekcja „Integracja z bazą danych” jest bezpłatna?

Tak — pełny tekst „Integracja z bazą danych” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.

Co nauczysz się w „Integracja z bazą danych”?

Trwałe przechowywanie danych Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?

Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Integracja z bazą danych”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?

Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Konfiguracja projektu
  2. Endpointy i modele
  3. Integracja z bazą danych
  4. Testowanie API
← Powrót do Learn Rust Coding