Introduction : Pourquoi Rust et les bases de données forment une combinaison idéale
Rust est un langage de programmation système moderne qui combine les performances du C/C++ avec la sécurité mémoire garantie par le compilateur. Lors du développement de services backend, d'applications web et de microservices, il est presque toujours nécessaire d'interagir avec des bases de données. Dans cet article, nous allons détailler comment travailler avec deux des types de bases de données les plus populaires : PostgreSQL (relationnelle) et MongoDB (orientée documents).
Nous aborderons la connexion, l'exécution d'opérations CRUD, les transactions et les agrégations à l'aide d'exemples pratiques. Tous les exemples de code sont écrits en Rust en utilisant le runtime asynchrone tokio, ce qui offre des performances maximales.
1. Configuration du projet et installation des dépendances
Pour commencer, créons un nouveau projet et ajoutons les crates nécessaires. Ouvrez un terminal et exécutez :
cargo new rust_db_examplecd rust_db_exampleAjoutons les dépendances dans le fichier Cargo.toml :
[dependencies]tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }mongodb = "2.8"serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"chrono = { version = "0.4", features = ["serde"] }La structure du projet est maintenant prête. Nous utiliserons sqlx pour PostgreSQL — un driver asynchrone avec prise en charge des requêtes compilées, et le driver officiel mongodb pour travailler avec MongoDB.
1.1. Connexion à PostgreSQL via sqlx
Écrivons un module pour travailler avec PostgreSQL. Créez le fichier src/postgres.rs :
use sqlx::postgres::PgPoolOptions;use sqlx::PgPool;use anyhow::Result;
pub async fn connect_to_postgres() -> Result<PgPool> { let database_url = "postgres://user:password@localhost:5432/mydb"; let pool = PgPoolOptions::new() .max_connections(5) .connect(database_url) .await?; println!("✅ Connecté à PostgreSQL"); Ok(pool)}
// Définition du modèle utilisateur#[derive(Debug, sqlx::FromRow)]pub struct User { pub id: i32, pub name: String, pub email: String, pub created_at: chrono::NaiveDateTime,}1.2. Connexion à MongoDB
Créez le fichier src/mongodb.rs :
use mongodb::{Client, Collection, Database};use mongodb::bson::{doc, Document};use anyhow::Result;
pub async fn connect_to_mongodb() -> Result<(Database, Collection<Document>)> { let client = Client::with_uri_str("mongodb://localhost:27017")?; let database = client.database("mydb"); let collection = database.collection::<Document>("users"); println!("✅ Connecté à MongoDB"); Ok((database, collection))}2. Opérations CRUD dans PostgreSQL
Implémentons maintenant les opérations de base : création, lecture, mise à jour et suppression d'enregistrements.
2.1. Insertion de données (CREATE)
pub async fn create_user(pool: &PgPool, name: &str, email: &str) -> Result<User> { let user = sqlx::query_as::<_, User>( "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email, created_at" ) .bind(name) .bind(email) .fetch_one(pool) .await?; println!("Utilisateur créé : {:?}", user); Ok(user)}2.2. Lecture de données (READ)
pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<Option<User>> { let user = sqlx::query_as::<_, User>( "SELECT id, name, email, created_at FROM users WHERE id = $1" ) .bind(user_id) .fetch_optional(pool) .await?; match user { Some(u) => println!("Utilisateur trouvé : {:?}", u), None => println!("Utilisateur avec id={} non trouvé"