Mongoose : CRUD et requêtes
Exemples avec les modèles Client et Commande (voir Schémas et modèles). Mongoose utilise des promesses : toutes les opérations s’écrivent avec await (les fonctions de rappel ne sont plus prises en charge).
const alice = await Client.create({ nom: 'Alice', email: 'alice@exemple.com', age: 30,});console.log(alice._id, alice.createdAt);
const lot = await Client.insertMany([ { nom: 'Bob', email: 'bob@exemple.com' }, { nom: 'Chloé', email: 'chloe@exemple.com' },]);
const dan = new Client({ nom: 'Dan', email: 'dan@exemple.com' });await dan.save();create et save appliquent la validation et les hooks ; insertMany valide aussi, mais n’exécute pas les hooks save.
const tous = await Client.find();const actifs = await Client.find({ actif: true });const unique = await Client.findOne({ email: 'alice@exemple.com' });const parId = await Client.findById('65f1c0a1b2c3d4e5f6a7b8c9');const existe = await Client.exists({ email: 'alice@exemple.com' }); // { _id } ou nullconst nombre = await Client.countDocuments({ actif: true });const estimation = await Client.estimatedDocumentCount(); // rapide, sans filtrefindOne et findById renvoient null si rien ne correspond : toujours tester le résultat.
Opérateurs de filtre
Section intitulée « Opérateurs de filtre »await Client.find({ age: { $gte: 18, $lt: 65 } });await Client.find({ nom: { $in: ['Alice', 'Bob'] } });await Client.find({ nom: /^a/i });await Client.find({ $or: [{ actif: false }, { age: { $lt: 18 } }] });await Client.find({ tags: 'promo' }); // tableau contenant « promo »await Client.find({ 'adresse.ville': 'Lyon' });Projection, tri, pagination
Section intitulée « Projection, tri, pagination »const page = await Client.find({ actif: true }) .select('nom email') // ou .select({ nom: 1, email: 1 }) ; .select('-tags') pour exclure .sort({ nom: 1 }) // 1 croissant, -1 décroissant .skip(20) .limit(10);Pour une grande collection, paginer par curseur de clé plutôt que skip (qui relit les documents ignorés) :
const suite = await Client.find({ _id: { $gt: dernierId } }) .sort({ _id: 1 }) .limit(20);lean() : lecture rapide
Section intitulée « lean() : lecture rapide »const liste = await Client.find().lean();lean() renvoie des objets JavaScript simples (sans méthodes, virtuels ni suivi de modifications) : bien plus rapide et léger pour des lectures destinées à être envoyées en JSON.
| Document Mongoose | lean() |
|
|---|---|---|
Méthodes, virtuels, save() |
Oui | Non |
| Mémoire et vitesse | Plus lourd | Léger |
| Usage | Modifier puis sauvegarder | Lecture seule |
Modifier
Section intitulée « Modifier »Charger puis enregistrer (validation et hooks complets)
Section intitulée « Charger puis enregistrer (validation et hooks complets) »const client = await Client.findById(id);if (!client) throw new Error('client introuvable');client.nom = 'Alice Martin';client.tags.push('fidèle');await client.save();Mise à jour directe
Section intitulée « Mise à jour directe »await Client.updateOne( { email: 'alice@exemple.com' }, { $set: { actif: false } },);await Client.updateMany({ actif: false }, { $set: { tags: [] } });
const maj = await Client.findByIdAndUpdate( id, { $set: { nom: 'Alice M.' } }, { returnDocument: 'after', runValidators: true },);| Option | Effet |
|---|---|
returnDocument: 'after' (ou new: true) |
Renvoie le document modifié (sinon l’ancien) |
runValidators: true |
Valide la mise à jour : non fait par défaut |
upsert: true |
Crée le document s’il n’existe pas |
Opérateurs de mise à jour courants :
| Opérateur | Effet |
|---|---|
$set, $unset |
Fixer, supprimer un champ |
$inc |
Incrémenter |
$push, $addToSet |
Ajouter à un tableau ($addToSet sans doublon) |
$pull |
Retirer d’un tableau |
$currentDate |
Date courante |
await Commande.updateOne( { _id }, { $inc: { totalCentimes: 500 }, $push: { lignes: { article: 'A12', quantite: 1, prixCentimes: 500 } }, },);Supprimer
Section intitulée « Supprimer »await Client.deleteOne({ email: 'dan@exemple.com' });await Client.deleteMany({ actif: false });const supprime = await Client.findByIdAndDelete(id); // renvoie le document suppriméProtéger les filtres : injection NoSQL
Section intitulée « Protéger les filtres : injection NoSQL »// Dangereux : req.body.email peut être { "$ne": null }const utilisateur = await Client.findOne({ email: req.body.email });
// Sûr : forcer une chaîneconst utilisateur = await Client.findOne({ email: String(req.body.email) });Un filtre reçu du client ({"email":{"$ne":null}}) peut contourner une vérification. Contrôler les types, valider avec une bibliothèque de schémas et activer sanitizeFilter :
mongoose.set('sanitizeFilter', true);Transactions
Section intitulée « Transactions »Les transactions exigent un ensemble de réplicas (replica set), pas un serveur MongoDB autonome :
const session = await mongoose.startSession();try { await session.withTransaction(async () => { await Commande.create([{ numero: 'C-1', totalCentimes: 1000 }], { session, }); await Client.updateOne({ _id }, { $inc: { achats: 1 } }, { session }); });} finally { await session.endSession();}Curseurs pour les gros volumes
Section intitulée « Curseurs pour les gros volumes »for await (const client of Client.find({ actif: true }).cursor()) { await traiter(client);}Le curseur lit les documents un par un, sans tout charger en mémoire.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
findOne renvoie null |
Filtre incorrect, mauvaise base : mongosh pour vérifier |
| Une mise à jour ne valide rien | runValidators: true |
CastError: Cast to ObjectId failed for value … |
Identifiant invalide : tester avec mongoose.isValidObjectId(id) |
E11000 duplicate key error |
Violation d’un index unique : traiter l’erreur (err.code === 11000) |
| Les méthodes du modèle sont absentes | Résultat de lean() |
| La mise à jour d’un tableau ne persiste pas | Modification d’un champ Mixed sans markModified, ou document non sauvegardé |
Transaction numbers are only allowed on a replica set member or mongos |
Pas de replica set : transactions indisponibles |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune propre à Ubuntu.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Requêtes
- Suite : Validation et hooks