0Pricing
Learn Rust Coding · Pelajaran

Integrasi Database

Menyimpan data secara persisten

Integrasi Database adalah pelajaran Learn Rust Coding gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Learn Rust Coding, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Learn Rust Coding mencakup 4 pelajaran total.

Menyimpan Data secara Persisten

Sejauh ini, API kita menyimpan data di memori, sehingga data hilang saat dimulai ulang. Layanan nyata memerlukan basis data. Dalam pelajaran ini, kita menghubungkan API Axum ke PostgreSQL menggunakan sqlx, toolkit SQL asinkron untuk Rust yang diperiksa saat kompilasi.

Anda akan mempelajari pool koneksi, query, pemetaan baris ke struct, dan penggunaan pool sebagai state bersama.

Menambahkan sqlx

Tambahkan sqlx dengan fitur yang diperlukan: runtime, TLS, dan driver basis data. Berikut penyiapan Postgres menggunakan Tokio.

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

Pool Koneksi

Membuka koneksi baru untuk setiap permintaan berjalan lambat. Pool koneksi menyimpan sekumpulan koneksi yang dapat digunakan kembali. PgPoolOptions membuatnya dari URL basis data. Pool ini murah untuk di-clone (secara internal menggunakan penghitungan referensi), sehingga ideal sebagai state bersama.

use sqlx::postgres::PgPoolOptions;

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

Pool sebagai State Aplikasi

Teruskan pool ke Axum dengan .with_state(pool). Handler kemudian menerima State(pool): State<PgPool>. Karena pool murah untuk di-clone, setiap permintaan berbagi koneksi dasar yang sama.

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

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

Menjalankan Query

Fungsi sqlx::query menjalankan SQL mentah. Ikat parameter dengan .bind(value) untuk mencegah injeksi SQL; Postgres menggunakan placeholder $1, $2. Gunakan .execute(&pool) untuk penulisan yang tidak mengembalikan baris.

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

Memetakan Baris ke Struct

Turunkan sqlx::FromRow pada model Anda agar hasil query dapat langsung dipetakan ke dalamnya. Gunakan query_as::<_, Todo> dengan fetch_all untuk memperoleh Vec<Todo>, atau fetch_one untuk satu baris.

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 yang Membaca Basis Data

Gabungkan semua bagian: handler mengambil pool dari state, menjalankan query, lalu mengembalikan JSON. Petakan error basis data ke status 500 agar klien menerima respons yang rapi, bukan 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),
    }
}

Mengembalikan Baris yang Disisipkan

Postgres dapat mengembalikan baris yang baru saja disisipkan dengan RETURNING. Gabungkan dengan query_as dan fetch_one untuk memperoleh record baru, termasuk id yang dibuat secara otomatis, dalam satu perjalanan pulang-pergi.

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

Migrasi

Skema harus tersedia sebelum query dijalankan. sqlx mendukung migrasi: file SQL dalam folder migrations/ yang diterapkan secara berurutan. Jalankan saat inisialisasi dengan sqlx::migrate!() agar basis data baru disiapkan secara otomatis.

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.

Transaksi

Ketika beberapa operasi penulisan harus berhasil bersama-sama, bungkus operasi tersebut dalam transaksi. Mulai dengan pool.begin(), jalankan query pada handle transaksi, lalu lakukan commit. Jika transaksi dihapus tanpa di-commit, sqlx secara otomatis melakukan rollback sehingga data tetap konsisten.

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

Konfigurasi dan Rahasia

Jangan pernah menulis kredensial basis data secara langsung dalam kode. Baca DATABASE_URL dari lingkungan, yang sering dimuat dari file .env dengan crate dotenvy selama pengembangan. Dalam produksi, platform menyediakannya sebagai variabel lingkungan.

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
}

Pemeriksaan Singkat

Uji pemahaman Anda tentang integrasi basis data.

Ringkasan

Anda telah mengintegrasikan basis data:

  • Gunakan kumpulan koneksi PgPool dan bagikan melalui .with_state.
  • query/execute untuk penulisan; query_as dengan FromRow untuk pembacaan bertipe.
  • Selalu ikat parameter untuk mencegah injeksi SQL.
  • RETURNING mengambil baris yang disisipkan; transaksi mengelompokkan penulisan secara atomik.
  • Jalankan migrasi saat memulai dan baca DATABASE_URL dari lingkungan.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Integrasi Database” gratis?

Ya — teks lengkap “Integrasi Database” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Learn Rust Coding, upgrade ke CoddyKit PRO. Kursus Learn Rust Coding mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Integrasi Database”?

Menyimpan data secara persisten Kamu berlatih Learn Rust Coding dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Learn Rust Coding?

Tidak diperlukan pengalaman sebelumnya. Learn Rust Coding di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Integrasi Database” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Learn Rust Coding ini?

Ya. Setiap pelajaran Learn Rust Coding menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Penyiapan Proyek
  2. Endpoint dan Model
  3. Integrasi Database
  4. Menguji API
← Kembali ke Learn Rust Coding