Intégration de la base de données
Conserver les données
Intégration de la base de données est une leçon Learn Rust Coding gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Learn Rust Coding, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Learn Rust Coding comprend 4 leçons au total.
Persister les données
Jusqu’ici, notre API stockait les données en mémoire, qui disparaissent au redémarrage. Les vrais services ont besoin d’une base de données. Dans cette leçon, nous connectons une API Axum à PostgreSQL avec sqlx, une boîte à outils SQL asynchrone pour Rust, vérifiée à la compilation.
Vous apprendrez à utiliser des pools de connexions et des requêtes, à mapper des lignes vers des structures et à utiliser le pool comme état partagé.
Ajouter sqlx
Ajoutez sqlx avec les fonctionnalités nécessaires : un environnement d’exécution, TLS et un pilote de base de données. Voici une configuration Postgres utilisant Tokio.
// Cargo.toml
// [dependencies]
// sqlx = { version = "0.7", features = [
// "runtime-tokio", "tls-rustls", "postgres", "macros"
// ] }Le pool de connexions
Ouvrir une nouvelle connexion pour chaque requête est lent. Un pool de connexions conserve un ensemble de connexions réutilisables. PgPoolOptions en construit un à partir d’une URL de base de données. Le pool est peu coûteux à copier, car il est compté par références en interne, ce qui en fait un état partagé idéal.
use sqlx::postgres::PgPoolOptions;
async fn make_pool(url: &str) -> sqlx::PgPool {
PgPoolOptions::new()
.max_connections(5)
.connect(url)
.await
.expect("failed to connect")
}Le pool comme état de l’application
Transmettez le pool à Axum avec .with_state(pool). Les gestionnaires prennent alors State(pool): State<PgPool>. Comme les copies du pool sont peu coûteuses, chaque requête partage les mêmes connexions sous-jacentes.
use axum::{routing::get, Router};
use sqlx::PgPool;
fn build(pool: PgPool) -> Router {
Router::new()
.route("/todos", get(list_todos))
.with_state(pool)
}Exécuter une requête
La fonction sqlx::query exécute du SQL brut. Liez les paramètres avec .bind(value) pour éviter les injections SQL ; Postgres utilise les paramètres de substitution $1 et $2. Utilisez .execute(&pool) pour les écritures qui ne renvoient pas de lignes.
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(())
}Mapper les lignes vers des structures
Dérivez sqlx::FromRow sur votre modèle afin que les résultats des requêtes y soient directement associés. Utilisez query_as::<_, Todo> avec fetch_all pour obtenir un Vec<Todo>, ou fetch_one pour une seule ligne.
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)
}Un gestionnaire qui lit la base de données
Combinez les éléments : un gestionnaire prend le pool depuis l’état, exécute une requête et renvoie du JSON. Convertissez les erreurs de base de données en statut 500 afin que le client reçoive une réponse propre plutôt qu’une interruption brutale.
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),
}
}Renvoyer les lignes insérées
Postgres peut renvoyer la ligne qu’il vient d’insérer avec RETURNING. Combinez-le avec query_as et fetch_one pour obtenir le nouvel enregistrement, y compris son identifiant généré, en un seul aller-retour.
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)
}Migrations
Votre schéma doit exister avant l’exécution des requêtes. sqlx prend en charge les migrations : des fichiers SQL dans un dossier migrations/, appliqués dans l’ordre. Exécutez-les au démarrage avec sqlx::migrate!() afin de configurer automatiquement une base de données vierge.
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.Transactions
Lorsque plusieurs écritures doivent réussir ensemble, regroupez-les dans une transaction. Commencez avec pool.begin(), exécutez les requêtes sur le handle de transaction, puis appelez commit. Si vous le supprimez sans valider la transaction, sqlx annule automatiquement les modifications et préserve la cohérence des données.
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(())
}Configuration et secrets
Ne codez jamais les identifiants de la base de données en dur. Lisez DATABASE_URL depuis l’environnement ; pendant le développement, cette variable est souvent chargée depuis un fichier .env avec la caisse dotenvy. En production, la plateforme l’injecte comme variable d’environnement.
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
}Vérification rapide
Vérifiez votre compréhension de l’intégration d’une base de données.
Récapitulatif
Vous avez intégré une base de données :
- Utilisez un pool de connexions
PgPoolet partagez-le avec.with_state. - Utilisez
query/executepour les écritures, etquery_asavecFromRowpour les lectures typées. - Liez toujours les paramètres pour empêcher les injections SQL.
RETURNINGrécupère les lignes insérées ; les transactions regroupent les écritures de manière atomique.- Exécutez les migrations au démarrage et lisez
DATABASE_URLdepuis l’environnement.
Questions Fréquemment Posées
La leçon « Intégration de la base de données » est-elle gratuite ?
Oui — le texte complet de « Intégration de la base de données » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Learn Rust Coding, passe à CoddyKit PRO. Le cours Learn Rust Coding comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Intégration de la base de données » ?
Conserver les données Tu pratiques Learn Rust Coding avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Learn Rust Coding ?
Aucune expérience préalable n'est requise. Learn Rust Coding sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Intégration de la base de données » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Learn Rust Coding ?
Oui. Chaque leçon Learn Rust Coding inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Configuration du projet
- Points d’accès et modèles
- Intégration de la base de données
- Tester l’API