Mongoose 27 termes

27 termes affichés

TypeBestPractice Concept Method Pattern
Niveau 🟢 Junior 🟡 Mid 🔴 Senior

Mis à jour ces 90 derniers jours 6 nouvelles · 3 révisées

Built-in Validators

Mongoose 🟢 Junior

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.

Anti-pattern : Désactiver la validation avec validateBeforeSave: false pour 'gagner en perf'.
#sgbd#interview#database#mongoose

Connection

Mongoose 🟢 Junior

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.

Anti-pattern : Créer une nouvelle connexion a chaque requête HTTP au lieu de réutiliser le pool.
#sgbd#interview#database#mongoose

Custom Validators

Mongoose 🟡 Mid

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.

#sgbd#interview#database#mongoose

Discriminators

Mongoose 🔴 Senior

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.

#sgbd#interview#database#mongoose

Document vs Query Middleware

Mongoose 🔴 Senior

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.

#sgbd#interview#database#mongoose

Embedded Discriminators

Mongoose 🔴 Senior

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.

Anti-pattern : Déclarer les hooks du schema après avoir créé le discriminator : ils ne s'appliqueront pas correctement.
#sgbd#interview#database#mongoose

Indexes in Mongoose

Mongoose 🟡 Mid

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.

Anti-pattern : Laisser Mongoose recréer tous les index a chaque redémarrage en prod avec autoIndex: true.
#sgbd#interview#database#mongoose

lean()

Mongoose 🟡 Mid

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.

Anti-pattern : Utiliser lean() puis essayer d'appeler .save() ou des méthodes d'instance Mongoose.
#sgbd#interview#database#mongoose

Middleware (Pre/Post Hooks)

Mongoose 🟡 Mid

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.

Anti-pattern : Oublier d'appeler next() dans un middleware pre, bloquant toute la chaîne d'exécution.
#sgbd#interview#database#mongoose

Model

Mongoose 🟢 Junior

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.

Anti-pattern : Appeler mongoose.model() plusieurs fois avec le même nom, causant une erreur OverwriteModelError.
#sgbd#interview#database#mongoose

Model.bulkWrite()

Mongoose 🟡 Mid

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.

Anti-pattern : Boucler sur des save() individuels pour un import de masse au lieu de bulkWrite(), multipliant les allers-retours réseau.
#sgbd#interview#database#mongoose

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 change

Cas d'usage : Éviter les écrasements concurrents sur un document a forte contention (stock, compteur partage).

Anti-pattern : Désactiver versionKey sur un modèle a forte concurrence d'écriture sans mécanisme de verrouillage alternatif.
#sgbd#interview#database#mongoose

Plugin

Mongoose 🟡 Mid

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.

#sgbd#interview#database#mongoose

populate()

Mongoose 🟡 Mid

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.

Anti-pattern : Chaîner plusieurs populate profonds (populate de populate) créant des dizaines de requêtes.
#sgbd#interview#database#mongoose

Pre Save Hook

Mongoose 🟡 Mid

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.

Anti-pattern : Modifier this dans un pre save avec une arrow function, qui ne lie pas le bon this.
#sgbd#interview#database#mongoose

Query Builder

Mongoose 🟡 Mid

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.

#sgbd#interview#database#mongoose

Query Cursor (streaming)

Mongoose 🔴 Senior

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.

Anti-pattern : Appeler .lean() puis manipuler le curseur comme un tableau charge en mémoire, perdant l'intérêt du streaming.
#sgbd#interview#database#mongoose

Schema

Mongoose 🟢 Junior

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.

Anti-pattern : Définir tous les champs comme Mixed ou SchemaTypes.Mixed, perdant tout l'intérêt de la validation.
#sgbd#interview#database#mongoose

SchemaType Options

Mongoose 🟢 Junior

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.

#sgbd#interview#database#mongoose

select()

Mongoose 🟢 Junior

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.

#sgbd#interview#database#mongoose

Sessions & Transactions

Mongoose 🔴 Senior

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.

Anti-pattern : Oublier de passer { session } a chaque opération de la transaction : l'opération s'exécute alors hors transaction.
#sgbd#interview#database#mongoose

Statics vs Methods

Mongoose 🟡 Mid

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.

#sgbd#interview#database#mongoose

strictQuery

Mongoose 🟡 Mid

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 silencieusement

Cas d'usage : Détecter des fautes de frappe dans les filtres de requête en passant strictQuery a 'throw' en développement.

Anti-pattern : Se fier a l'ancien comportement true (filtrage silencieux) sans vérifier le défaut de la version installée.
#sgbd#interview#database#mongoose

Timestamps

Mongoose 🟢 Junior

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 automatiquement

Cas d'usage : Tracer quand chaque document a été créé et modifie pour l'audit et le debug.

#sgbd#interview#database#mongoose

toJSON / toObject Transform

Mongoose 🟡 Mid

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.

#sgbd#interview#database#mongoose

Virtual Populate

Mongoose 🔴 Senior

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.

#sgbd#interview#database#mongoose

Virtual Properties

Mongoose 🟡 Mid

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.

Anti-pattern : Oublier { toJSON: { virtuals: true } } dans le schema, les virtuals n'apparaissent pas dans les réponses API.
#sgbd#interview#database#mongoose