Mis à jour ces 90 derniers jours 6 nouvelles · 3 révisées
Validateurs integres a Mongoose : required, min/max (Number), minlength/maxlength (String), enum, match (regex). Ils s'exécutent automatiquement avant la sauvegarde.
Un videur qui vérifie ta carte d'identité, ta tenue et ta réservation avant de te laisser entrer.
{
age: { type: Number, min: 0, max: 150 },
role: { type: String, enum: ['user', 'admin'] },
name: { type: String, minlength: 2 }
}Cas d'usage : Valider les données utilisateur avant de les persister en base sans logique custom.
Objet qui gère la connexion a MongoDB. Mongoose maintient un pool de connexions et un buffer des opérations en attente jusqu'a ce que la connexion soit établie.
Le fil du téléphone entre ton appli et la base de données, avec une ligne d'attente intégrée.
await mongoose.connect(
'mongodb://localhost:27017/mydb',
{ maxPoolSize: 10 }
);
mongoose.connection.on('error', console.error);Cas d'usage : Initialiser la connexion a la base au démarrage de l'application NestJS ou Express.
Fonctions de validation personnalisées définies dans le schema. Peuvent être synchrones ou asynchrones et retournent un booléen ou lancent une erreur.
Un test sur mesure en plus des contrôles standard : vérifier que le code postal correspond bien a la ville.
{
phone: {
type: String,
validate: {
validator: (v) => /^\+33[0-9]{9}$/.test(v),
message: 'Format FR invalide'
}
}
}Cas d'usage : Valider des règles métier spécifiques comme un format de téléphone, une plage de dates ou un IBAN.
Mécanisme d'héritage de schema qui permet de stocker des documents de types différents dans une même collection, avec un champ discriminateur (__t) pour différencier les types. Les discriminators peuvent aussi s'appliquer a un tableau de sous-documents embarque (embedded discriminators) plutôt qu'a une collection racine entière.
Un parking multi-véhicules : voitures, motos et camions partagent le même espace mais ont des règles différentes.
const Event = mongoose.model('Event', eventSchema);
const Click = Event.discriminator('Click',
new Schema({ url: String })
);
const Purchase = Event.discriminator('Purchase',
new Schema({ amount: Number })
);Cas d'usage : Modéliser un système d'evenements (analytics) ou des entités polymorphiques dans une seule collection.
Les Document middlewares (save, validate, remove) ont this = le document. Les Query middlewares (find, update, delete) ont this = la query. Important pour savoir quoi modifier.
Document middleware = le chirurgien qui opère directement le patient. Query middleware = le pharmacien qui modifie l'ordonnance.
// Document: this = document
schema.pre('save', function() {
console.log(this.name);
});
// Query: this = query
schema.pre('find', function() {
this.where({ deleted: false });
});Cas d'usage : Implémenter du soft-delete en filtrant automatiquement les documents supprimes sur chaque find.
Variante des discriminators appliquée a un tableau de sous-documents plutôt qu'a une collection racine : elle permet de stocker plusieurs schémas différents dans le même tableau embarque d'un document, chacun identifie par sa propre clé de discrimination (__t).
Comme une boite a outils unique qui accepte des compartiments de formes différentes, chacun étiqueté selon son contenu.
const eventSchema = new Schema(
{ message: String }, { discriminatorKey: '__t' }
);
const batchSchema = new Schema({ events: [eventSchema] });
const eventsArray = batchSchema.path('events');
eventsArray.discriminator('Clicked', new Schema({ url: String }));
eventsArray.discriminator('Purchased', new Schema({ amount: Number }));Cas d'usage : Modéliser un journal d'evenements hétérogènés (clic, achat, connexion) dans un seul tableau embarque.
Mongoose permet de définir les index directement dans le schema avec index: true, unique: true ou via schema.index(). Les index sont synchronises au démarrage de l'app.
Déclarer les index dans le plan (schema) pour que l'architecte (MongoDB) les construise automatiquement.
userSchema.index(
{ email: 1 },
{ unique: true }
);
userSchema.index(
{ lastName: 1, firstName: 1 }
);Cas d'usage : Gérer les index dans le code applicatif pour les versionner avec Git.
Méthode qui retourne des objets JavaScript bruts au lieu d'instances Mongoose. 2 a 5x plus rapide car elle saute l'hydratation, les getters et les virtuals. Mongoose 9 (dernière version stable 9.10.2) s'appuie sur le driver MongoDB Node.js 7.6 ; le comportement de lean() reste identique, y compris combine a un curseur en streaming via cursor().
Commander un plat a emporter sans assiette ni couverts : plus léger et plus rapide a servir.
const users = await User
.find({ status: 'active' })
.lean();
// users[0].save() -> Error (pas un doc Mongoose)Cas d'usage : API en lecture seule ou les données sont serialisees en JSON sans modification.
Fonctions exécutées avant (pre) ou après (post) certaines opérations comme save, validate, remove ou find. Permettent d'injecter de la logique transversale.
Les contrôles de sécurité a l'aéroport (pre) et le tapis a bagages (post) : ils s'exécutent automatiquement autour de ton vol.
userSchema.pre('save', async function() {
if (this.isModified('password')) {
this.password = await bcrypt.hash(
this.password, 10
);
}
});Cas d'usage : Hasher les mots de passe avant sauvegarde, logger les opérations ou mettre a jour des timestamps.
Classe construite a partir d'un Schema qui fournit l'interface CRUD pour interagir avec une collection MongoDB. Chaque instance d'un Model est un document.
Le moule a gâteau (Model) créé a partir du plan (Schema) : chaque gâteau (document) a la même forme.
const User = mongoose.model('User', userSchema);
const alice = new User({ name: 'Alice' });
await alice.save();Cas d'usage : Créer le point d'entrée pour toutes les opérations sur une collection dans une app Node.js.
Méthode Mongoose qui exécute plusieurs opérations d'écriture (insertOne, updateOne, deleteOne...) en un seul aller-retour réseau, en appliquant le casting et la validation du schema a chaque opération avant de déléguer au bulkWrite() du driver MongoDB natif.
Comme déposer plusieurs colis en une seule tournée du facteur plutôt qu'un aller-retour par colis.
await User.bulkWrite([
{ insertOne: { document: { name: 'A', age: 30 } } },
{ updateOne: {
filter: { name: 'B' },
update: { $set: { age: 31 } }
}},
{ deleteOne: { filter: { name: 'C' } } }
]);Cas d'usage : Importer ou synchroniser un gros volume de documents avec des opérations mixtes en une seule requête.
Mongoose ajoute par défaut un champ __v (versionKey) incrémenté a chaque modification d'un tableau. Combine a l'option optimisticConcurrency: true du schema, save() échoué avec une VersionError si le document a été modifie entre la lecture et l'écriture, évitant d'écraser silencieusement les changements d'un tiers.
Comme éditer un document partage qui refuse ta sauvegarde si quelqu'un d'autre l'a déjà modifie entre-temps, plutôt que d'écraser son travail.
const schema = new Schema(
{ stock: Number },
{ optimisticConcurrency: true }
);
const Product = mongoose.model('Product', schema);
const p = await Product.findById(id);
p.stock -= 1;
await p.save(); // VersionError si __v a changeCas d'usage : Éviter les écrasements concurrents sur un document a forte contention (stock, compteur partage).
Fonction réutilisable qui ajoute des fonctionnalités a un schema ou a tous les schémas globalement. Permet d'encapsuler des comportements comme le soft-delete, la pagination ou l'audit.
Une extension pour navigateur : tu l'installes une fois et tous tes onglets en bénéficient.
function timestampPlugin(schema) {
schema.add({ createdAt: Date, updatedAt: Date });
schema.pre('save', function() {
this.updatedAt = new Date();
});
}
mongoose.plugin(timestampPlugin);Cas d'usage : Partager des comportements communs entre plusieurs schémas sans dupliquer le code.
Méthode qui remplace les ObjectId références par les documents complets de la collection liée. Équivalent d'un JOIN automatique gère par Mongoose. L'option options.session permet d'inclure un populate() a l'intérieur d'une transaction MongoDB, et match filtre les documents références sans filtrer le document parent.
Cliquer sur un lien hypertexte pour voir le contenu complet au lieu de juste le numéro de référence.
const order = await Order
.findById(id)
.populate('customer', 'name email')
.populate('products');Cas d'usage : Charger les données liées (auteur d'un article, produits d'une commande) en une seule requête logique.
Middleware exécuté avant chaque appel a document.save(). Permet de modifier le document (hashing, timestamps, validation custom) avant sa persistence en base.
Le dernier contrôle qualité sur la chaîne de montage avant de mettre le produit en boite.
schema.pre('save', function(next) {
if (!this.slug) {
this.slug = this.title
.toLowerCase()
.replace(/\s+/g, '-');
}
next();
});Cas d'usage : Générer automatiquement un slug, un hash de mot de passe ou un timestamp a chaque sauvegarde.
API chainable de Mongoose pour construire des requêtes incrementalement avec where(), equals(), gt(), sort(), limit(), etc. La requête n'est exécutée qu'a l'appel de exec() ou await.
Un panier de courses : tu ajoutes des articles au fur et a mesure, et tu payes (exec) seulement a la fin.
const users = await User
.where('age').gte(18)
.where('status').equals('active')
.sort('-createdAt')
.limit(10)
.exec();Cas d'usage : Construire des requêtes dynamiques en fonction des paramètrès de filtre d'une API.
Query#cursor() retourne un curseur Node.js en mode streaming au lieu de charger tous les documents en mémoire via exec(). Chaque document est hydraté en instance Mongoose au fil de l'eau, ce qui permet de traiter des collections volumineuses sans saturer la RAM du processus.
Comme lire un livre page par page plutôt que de charger toutes les pages en mémoire avant de commencer a lire.
const cursor = User.find({ active: true }).cursor();
for await (const user of cursor) {
await processUser(user); // un document a la fois
}Cas d'usage : Exporter ou traiter en masse une collection de plusieurs millions de documents sans épuiser la mémoire.
Définition de la structure d'un document Mongoose : types de champs, validations, valeurs par défaut et options. Le schema est la base pour créer un Model.
Le plan d'architecte d'une maison : il définit chaque pièce avant même de poser la première brique.
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, unique: true },
age: { type: Number, min: 0 },
role: { type: String, enum: ['user', 'admin'] }
});Cas d'usage : Définir une structure stricte pour les documents MongoDB dans une application Node.js.
Options de configuration par champ dans un Schema : type, required, default, unique, index, lowercase, uppercase, trim, minlength, maxlength, enum, validate.
Les attributs d'un champ de formulaire HTML : requis, longueur max, format email, etc.
{
email: {
type: String,
required: [true, 'Email requis'],
lowercase: true,
trim: true,
match: /^\S+@\S+$/
}
}Cas d'usage : Définir les contraintes et transformations de chaque champ directement dans le schema.
Méthode de query qui spécifie les champs a inclure ou exclure du résultat. Équivalent de la projection MongoDB mais avec une syntaxe Mongoose.
Cocher les colonnes que tu veux voir dans un tableur avant d'exporter.
const user = await User
.findById(id)
.select('name email -_id');Cas d'usage : Ne retourner que les champs nécessaires dans une réponse API pour réduire la bande passante.
Mongoose expose les transactions MongoDB via mongoose.startSession() puis session.withTransaction(), qui gère automatiquement commit/abort et les retries sur erreurs transitoires. Connection#transaction() est un wrapper Mongoose qui réinitialisé l'état des documents modifies en cas d'échec, pour permettre une nouvelle tentative de save().
Comme un brouillon qu'on jette entièrement et qu'on recommence depuis zéro si l'écriture finale au propre échoué, plutôt que de laisser des ratures.
const session = await mongoose.startSession();
await session.withTransaction(async () => {
await Account.updateOne(
{ _id: 'A' }, { $inc: { bal: -100 } }, { session }
);
await Account.updateOne(
{ _id: 'B' }, { $inc: { bal: 100 } }, { session }
);
});
await session.endSession();Cas d'usage : Garantir l'atomicité d'un virement entre deux documents Mongoose distincts.
Statics sont des méthodes sur le Model (niveau classe). Methods sont des méthodes sur l'instance du document. Permettent d'ajouter de la logique métier au modèle.
Statics = méthode de classe ('User.findByEmail'). Methods = méthode d'instance ('alice.comparePassword').
// Static (sur le Model)
userSchema.statics.findByEmail = function(email) {
return this.findOne({ email });
};
// Method (sur le document)
userSchema.methods.isAdmin = function() {
return this.role === 'admin';
};Cas d'usage : Encapsuler la logique métier dans le modèle au lieu de la disperser dans les services.
Option de schema qui contrôle le comportement des filtres de requête (find, findOne...) sur des champs absents du schema, a distinguer de l'option strict qui ne s'applique qu'aux écritures. Depuis Mongoose 7, strictQuery vaut false par défaut : les champs inconnus dans un filtre sont conserves tels quels plutôt que supprimes silencieusement.
Comme un videur qui, avant, ignorait silencieusement les noms non présents sur la liste (true) et qui aujourd'hui les laisse passer sans les filtrer (false) : le comportement par défaut a change.
mongoose.set('strictQuery', false); // defaut depuis Mongoose 7
// 'throw' leve une erreur si un champ hors
// schema est utilise dans le filtre
const User = mongoose.model('User', schema);
await User.find({ champInexistant: 'x' });
// pas filtre silencieusementCas d'usage : Détecter des fautes de frappe dans les filtres de requête en passant strictQuery a 'throw' en développement.
Option de schema qui ajoute automatiquement createdAt et updatedAt sur chaque document. updatedAt est mis a jour automatiquement a chaque modification.
Le tampon dateur du bureau de poste : chaque lettre reçoit la date d'envoi et de réception.
const schema = new Schema(
{ name: String },
{ timestamps: true }
);
// Cree createdAt et updatedAt automatiquementCas d'usage : Tracer quand chaque document a été créé et modifie pour l'audit et le debug.
Options de schema qui permettent de transformer la sortie JSON d'un document. Utile pour supprimer des champs sensibles ou renommer _id en id.
Un filtre photo automatique : chaque fois que tu exportes la photo, le filtre s'applique sans effort.
schema.set('toJSON', {
virtuals: true,
transform: (doc, ret) => {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
delete ret.password;
}
});Cas d'usage : Formater automatiquement les réponses API en supprimant _id, __v et les champs sensibles.
Permet de populer une relation sans stocker de référence dans le document parent. Utilise un champ du document enfant comme foreignField pour la jointure.
Trouver tous les colis livres a une adresse sans que l'adresse ait une liste de colis.
authorSchema.virtual('books', {
ref: 'Book',
localField: '_id',
foreignField: 'author'
});
await Author.findById(id).populate('books');Cas d'usage : Créer une relation one-to-many sans tableau de références dans le parent, évitant les documents trop gros.
Propriétés calculées qui n'existent pas en base mais sont disponibles sur le document Mongoose. Définies via des getters et setters sur le schema.
L'âge d'une personne : il n'est pas stocke, il est calcule a partir de la date de naissance.
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
const user = await User.findById(id);
console.log(user.fullName);Cas d'usage : Créer des champs derives comme fullName, âge ou un URL complète sans les stocker en base.