
Création, lecture, mise à jour et suppression de données (CRUD)
Apprenez a effectuer les operations CRUD (Create, Read, Update, Delete) essentielles en Node.js, que ce soit avec MySQL (mysql2) ou MongoDB (pilote natif / Mongoose).
Le cycle de vie fondamental des donnees : CRUD
Une fois la connexion à votre base de données établie (que ce soit SQL ou NoSQL), l'étape suivante consiste à interagir avec les données elles-mêmes. Les opérations fondamentales que l'on effectue sur des données persistantes sont regroupées sous l'acronyme CRUD :
- Create (Créer) : Ajouter de nouvelles données (enregistrements, documents) dans la base.
- Read (Lire) : Récupérer ou rechercher des données existantes dans la base.
- Update (Mettre à jour) : Modifier des données existantes.
- Delete (Supprimer) : Enlever des données de la base.
Ces quatre opérations constituent le coeur de la plupart des applications qui manipulent des données. Que vous construisiez une API RESTful, un site e-commerce, un blog, ou un réseau social, vous implémenterez constamment des fonctionnalités qui se traduisent par une ou plusieurs de ces opérations CRUD.
Ce chapitre montre comment réaliser ces opérations CRUD en Node.js, en illustrant les syntaxes pour les deux types de bases de données que nous avons abordés : MySQL (via `mysql2/promise`) et MongoDB (via le pilote natif `mongodb` et l'ODM `Mongoose`).
Operations CRUD avec MySQL (`mysql2/promise`)
Avec MySQL, les opérations CRUD se traduisent par des requêtes SQL spécifiques, exécutées via `pool.execute()` (recommandé pour la sécurité et la performance avec les instructions préparées).
1. Create (INSERT) : Ajouter une nouvelle ligne dans une table.
async function createUser(pool, username, email, passwordHash) {
const sql = 'INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)';
try {
const [result] = await pool.execute(sql, [username, email, passwordHash]);
console.log('Utilisateur inséré avec ID:', result.insertId);
return result.insertId; // Retourne l'ID auto-incrémenté
} catch (error) {
console.error('Erreur INSERT:', error);
throw error;
}
}
2. Read (SELECT) : Récupérer des données. Peut être une ligne spécifique ou plusieurs lignes.
// Lire un utilisateur par ID (vu précédemment)
async function getUserById(pool, userId) {
const sql = 'SELECT id, username, email FROM users WHERE id = ?';
const [rows] = await pool.execute(sql, [userId]);
return rows.length > 0 ? rows[0] : null;
}
// Lire tous les utilisateurs (exemple simple, attention à la pagination en réel)
async function getAllUsers(pool) {
const sql = 'SELECT id, username, email FROM users ORDER BY username';
const [rows] = await pool.query(sql); // query() suffit si pas de paramètres
return rows;
}
3. Update (UPDATE) : Modifier une ou plusieurs lignes existantes.
async function updateUserEmail(pool, userId, newEmail) {
const sql = 'UPDATE users SET email = ? WHERE id = ?';
try {
const [result] = await pool.execute(sql, [newEmail, userId]);
console.log('Lignes affectées par UPDATE:', result.affectedRows);
return result.affectedRows > 0; // Retourne true si au moins une ligne a été modifiée
} catch (error) {
console.error('Erreur UPDATE:', error);
throw error;
}
}
4. Delete (DELETE) : Supprimer une ou plusieurs lignes.
async function deleteUser(pool, userId) {
const sql = 'DELETE FROM users WHERE id = ?';
try {
const [result] = await pool.execute(sql, [userId]);
console.log('Lignes affectées par DELETE:', result.affectedRows);
return result.affectedRows > 0; // Retourne true si au moins une ligne a été supprimée
} catch (error) {
console.error('Erreur DELETE:', error);
throw error;
}
}
Notez l'utilisation systématique de `pool.execute()` avec des placeholders `?` pour passer les valeurs dynamiques, prévenant ainsi les injections SQL.
Operations CRUD avec MongoDB (Pilote Natif `mongodb`)
Avec le pilote natif MongoDB, les opérations CRUD sont effectuées via des méthodes sur l'objet `collection` (obtenu via `db.collection(...)`).
1. Create (insertOne, insertMany) : Ajouter un ou plusieurs documents.
async function createProduct(collection, productData) {
try {
const result = await collection.insertOne(productData);
console.log('Produit inséré avec ID:', result.insertedId);
return result.insertedId;
} catch (error) {
console.error('Erreur insertOne:', error);
throw error;
}
}
2. Read (findOne, find) : Récupérer un ou plusieurs documents.
// Lire un produit par son _id (ObjectId)
const { ObjectId } = require('mongodb'); // Nécessaire pour convertir l'ID string en ObjectId
async function getProductById(collection, productId) {
try {
const product = await collection.findOne({ _id: new ObjectId(productId) });
return product;
} catch (error) { /* ... */ }
}
// Lire tous les produits d'une catégorie (retourne un curseur)
async function getProductsByCategory(collection, category) {
try {
const cursor = collection.find({ category: category });
// Pour obtenir un tableau, il faut itérer ou utiliser toArray()
const products = await cursor.toArray();
return products;
} catch (error) { /* ... */ }
}
3. Update (updateOne, updateMany) : Modifier un ou plusieurs documents. Nécessite un filtre pour sélectionner les documents et un document de mise à jour (utilisant des opérateurs comme `$set`, `$inc`, etc.).
async function updateProductPrice(collection, productId, newPrice) {
try {
const result = await collection.updateOne(
{ _id: new ObjectId(productId) }, // Filtre
{ $set: { price: newPrice, lastUpdated: new Date() } } // Opération de mise à jour
);
console.log('Documents correspondants:', result.matchedCount, 'Documents modifiés:', result.modifiedCount);
return result.modifiedCount > 0;
} catch (error) { /* ... */ }
}
4. Delete (deleteOne, deleteMany) : Supprimer un ou plusieurs documents. Nécessite un filtre.
async function deleteProduct(collection, productId) {
try {
const result = await collection.deleteOne({ _id: new ObjectId(productId) });
console.log('Documents supprimés:', result.deletedCount);
return result.deletedCount > 0;
} catch (error) { /* ... */ }
}
Operations CRUD avec MongoDB (Mongoose)
Mongoose simplifie et structure ces opérations en utilisant des Modèles définis à partir de Schémas. Les méthodes CRUD sont appelées directement sur le Modèle ou sur une instance de Modèle.
Supposons un Modèle `Product` défini à partir d'un Schéma Mongoose.
1. Create (Model.create ou new Model().save()) :
const Product = require('./models/Product'); // Importer le modèle Mongoose
async function createProductMongoose(productData) {
try {
// Méthode 1: Model.create()
const newProduct = await Product.create(productData);
// Méthode 2: new Model().save()
// const product = new Product(productData);
// const newProduct = await product.save();
console.log('Produit créé (Mongoose):', newProduct);
return newProduct;
} catch (error) {
console.error('Erreur création Mongoose:', error); // La validation du schéma peut lever des erreurs
throw error;
}
}
2. Read (Model.find, Model.findById, Model.findOne) :
// Lire un produit par ID (Mongoose gère la conversion en ObjectId)
async function getProductByIdMongoose(productId) {
try {
const product = await Product.findById(productId);
return product;
} catch (error) { /* ... */ }
}
// Lire tous les produits d'une catégorie (API de requêtes riche)
async function getProductsByCategoryMongoose(category) {
try {
const products = await Product.find({ category: category })
.sort({ name: 1 }) // Tri
.limit(10); // Pagination
return products;
} catch (error) { /* ... */ }
}
3. Update (Model.updateOne, Model.findByIdAndUpdate, instance.save()) :
// Mettre à jour en utilisant l'ID
async function updateProductPriceMongoose(productId, newPrice) {
try {
// Méthode 1: findByIdAndUpdate (retourne l'objet avant ou après mise à jour)
const updatedProduct = await Product.findByIdAndUpdate(
productId,
{ $set: { price: newPrice, lastUpdated: new Date() } },
{ new: true } // Option pour retourner le document modifié
);
console.log('Produit mis à jour:', updatedProduct);
return updatedProduct;
// Méthode 2: findById -> modifier -> instance.save() (utile si logique complexe ou hooks Mongoose)
// const product = await Product.findById(productId);
// if (!product) return null;
// product.price = newPrice;
// product.lastUpdated = new Date();
// const savedProduct = await product.save();
// return savedProduct;
} catch (error) { /* ... */ }
}
4. Delete (Model.deleteOne, Model.findByIdAndDelete, instance.remove()) :
async function deleteProductMongoose(productId) {
try {
// Méthode 1: findByIdAndDelete
const result = await Product.findByIdAndDelete(productId);
console.log('Produit supprimé (null si non trouvé):', result);
return result;
// Méthode 2: deleteOne avec filtre
// const deleteResult = await Product.deleteOne({ _id: productId });
// console.log('Résultat suppression:', deleteResult);
// return deleteResult.deletedCount > 0;
} catch (error) { /* ... */ }
}
Mongoose offre une API plus abstraite et souvent plus concise pour les opérations CRUD, en plus de la validation de schéma et d'autres fonctionnalités.
Conclusion : la base de l'interaction avec les donnees
Les opérations CRUD sont le pain quotidien du développement back-end lorsqu'il s'agit de gérer des données persistantes. Que vous utilisiez une base de données SQL comme MySQL avec `mysql2` ou une base NoSQL comme MongoDB avec le pilote natif ou Mongoose, les concepts fondamentaux restent les mêmes : créer, lire, mettre à jour et supprimer.
Maîtriser la syntaxe spécifique à votre base de données et à la bibliothèque Node.js que vous utilisez est essentiel. N'oubliez jamais la sécurité (prévention des injections SQL, validation des données pour NoSQL), la gestion des erreurs et l'utilisation des approches asynchrones (Promesses, `async/await`) pour maintenir la performance de votre application Node.js.