3.3MongoDB & Mongoose : base NoSQL
L'essentiel en 30 secondes
- MongoDB stocke des documents : objets type JSON/BSON en collections, sans schéma rigide — proche des objets JavaScript.
- Mongoose ajoute la rigueur : schéma (types + validations required/unique) puis modèle offrant les méthodes de manipulation.
- Le CRUD est asynchrone : create, find/findById, findByIdAndUpdate, findByIdAndDelete avec await, plus requêtes, populate et hooks.
Mini-cas filé — Salim stocke son catalogue sans chaos
Salim, développeur junior à Alger, gardait son catalogue dans un tableau en mémoire : tout disparaît à chaque redémarrage. Il passe à MongoDB : connexion via mongoose.connect avec une URI factice de travail, schéma produit (nom requis, prix nombre positif, catégorie par défaut), modèle Produit, puis CRUD complet en await. Quand un champ email manque ou qu'un prix est négatif, la validation Mongoose rejette avant enregistrement. Fini les données fantaisistes.
Ce même cas revient dans le cours, le schéma, le script et le défi 48h.
1. MongoDB, base orientée documents
Une appli réelle doit persister ses données : MongoDB, base NoSQL orientée documents très populaire avec Node, stocke des documents (structures type JSON/BSON) regroupés en collections — pas de tables ni de lignes, pas de schéma rigide imposé par la base. Un document ressemble à { nom: 'Théière', prix: 2400 } : flexible, imbricable, naturel pour un développeur JavaScript.
Cette flexibilité est un atout (structures évolutives, prototypage rapide) mais aussi un risque : sans discipline, les documents divergent (champs renommés, types mélangés). D'où la couche Mongoose — et le choix NoSQL vs SQL (section 3-4) selon la nature des données : flexibles et peu relationnelles → MongoDB ; fortement liées et critiques → SQL.
2. Mongoose : schémas, modèles, CRUD async
On travaille via Mongoose, un ODM qui habille MongoDB. Le schéma définit la forme attendue avec types et validations : new mongoose.Schema({ nom: String, email: { type: String, required: true, unique: true } }). Le modèle créé depuis le schéma (mongoose.model('User', userSchema)) offre les méthodes : create / new + save, find / findById, findByIdAndUpdate, findByIdAndDelete — toutes asynchrones, donc avec await.
Mongoose gère aussi les requêtes (filtres, tri, pagination), les relations (références + populate pour joindre) et les hooks (exécuter du code avant/après, comme hacher un mot de passe avant enregistrement — section 4-2). Réflexe de Salim : toujours définir un schéma même si MongoDB ne l'exige pas, et jamais de mot de passe ni d'URI réelle dans le code partagé.
🔎 Exemple concret — Schéma, modèle et CRUD validé
Salim persiste son premier produit avec validation.
const mongoose = require('mongoose');
// URI FACTICE pédagogique — jamais d'URI réelle dans le code partagé
await mongoose.connect('mongodb://127.0.0.1:27017/demo-catalogue');
const produitSchema = new mongoose.Schema({
nom: { type: String, required: true },
prix: { type: Number, min: 0 }
});
const Produit = mongoose.model('Produit', produitSchema);
const p = await Produit.create({ nom: 'Théière', prix: 2400 });
const lu = await Produit.findById(p._id);
Lecture : connexion, schéma validé, modèle, puis create/findById en await.
Leçon : une donnée invalide est rejetée par le schéma avant la base.
Vocabulaire de la section
- Persistance
- Sauvegarde durable des données dans une base (utilisateurs, produits…) au lieu de la mémoire volatile.
Exemple simple : Le catalogue de Salim conservé après redémarrage.
Ce que ce n'est pas : un tableau JS vidé à chaque arrêt du serveur.
Réflexe : persister dès que la donnée doit survivre.
- MongoDB (NoSQL document)
- Base stockant des documents type JSON/BSON en collections, sans schéma rigide ; flexible et proche du JS.
Exemple simple : La collection produits aux documents variés.
Ce que ce n'est pas : une table SQL à colonnes strictes et jointures.
Réflexe : l'envisager pour données flexibles et évolutives.
- Mongoose (ODM)
- Couche pratique sur MongoDB : schémas validés, modèles, requêtes, relations et hooks.
Exemple simple : Le schéma produit de Salim avec required et min.
Ce que ce n'est pas : le driver brut sans validation ni modèles.
Réflexe : passer par Mongoose pour la rigueur quotidienne.
- Schéma & modèle
- Le schéma définit structure et validations ; le modèle, créé depuis le schéma, offre les méthodes CRUD.
Exemple simple : produitSchema puis mongoose.model('Produit', produitSchema).
Ce que ce n'est pas : des documents jetés en base sans forme déclarée.
Réflexe : déclarer types, required et unique avant le premier create.
- CRUD async
- Create, Read, Update, Delete via méthodes Mongoose asynchrones (await) : create, find, findByIdAndUpdate, findByIdAndDelete.
Exemple simple : await Produit.create(...) puis findById.
Ce que ce n'est pas : des appels synchrones imaginaires sans await.
Réflexe : await systématique et gestion d'erreur try/catch.
En pratique — Persister des données avec MongoDB et Mongoose
- Connectez-vous à MongoDB avec mongoose.connect(uri) (base locale ou MongoDB Atlas en cloud, URI factice en exercice).
- Définissez un schéma (types + validations : required, unique) et créez un modèle avec mongoose.model('User', userSchema).
- Réalisez un CRUD complet (asynchrone, avec await) : create (créer), find/findById (lire), findByIdAndUpdate (modifier), findByIdAndDelete (supprimer).
- Explorez les requêtes (filtres, tri, pagination) et une relation entre documents (référence + populate).
Script prêt à dire — présenter sa persistance Mongo (30 s)
« Le catalogue est persisté sous MongoDB : connexion mongoose, schéma produit avec nom requis et prix positif, modèle Produit, CRUD complet en await. La validation rejette les données invalides avant la base, et je teste sur une URI locale factice, jamais de secret réel dans le code. »
À dire en démo ; montrer un create rejeté par la validation.
Modèle copier-coller — Mongoose CRUD (URI factice)
const mongoose = require('mongoose');
// URI FACTICE — exemple pédagogique, remplacer en local, secret en variable d'environnement
const URI_FACTICE = 'mongodb://127.0.0.1:27017/demo-catalogue';
async function main() {
await mongoose.connect(URI_FACTICE);
const produitSchema = new mongoose.Schema({
nom: { type: String, required: true },
prix: { type: Number, min: 0, required: true }
});
const Produit = mongoose.model('Produit', produitSchema);
const cree = await Produit.create({ nom: 'Théière', prix: 2400 });
const lu = await Produit.findById(cree._id);
await Produit.findByIdAndUpdate(cree._id, { prix: 2600 });
await Produit.findByIdAndDelete(cree._id);
await mongoose.disconnect();
}
main().catch(console.error);
Indépendant
Un seul modèle Mongoose validé suffit à fiabiliser les devis stockés.
PME à Alger
Salim modélise produits et avis liés, avec populate pour afficher les avis par produit.
Équipe produit
Imposer required/unique au schéma pour que chaque équipe écrive des données cohérentes.
Débutant
Cette semaine : un schéma à deux champs et un CRUD create + find en await.
✓ Bonnes pratiques
- Toujours définir un schéma Mongoose (rigueur + validation).
- Utiliser le CRUD asynchrone avec await.
- Hacher les mots de passe via un hook avant enregistrement (voir 4.2).
✗ Pièges fréquents
- Renoncer au schéma sous prétexte que MongoDB est « flexible » (données chaotiques).
- Accepter n'importe quel champ envoyé par un client (risque d'injection de champs).
- Oublier que les méthodes Mongoose sont asynchrones (await).
Points clés à retenir
- Une application réelle PERSISTE ses données. Deux familles : NoSQL (MongoDB) et SQL/relationnel (3-4).
- MongoDB (NoSQL orienté DOCUMENTS) stocke des documents type JSON/BSON en COLLECTIONS — flexible, proche des objets JS.
- MONGOOSE (ODM) ajoute schémas (structure + VALIDATION) et modèles (méthodes) : rigueur ET souplesse.
- CRUD asynchrone (await) : create / find(ById) / findByIdAndUpdate / findByIdAndDelete ; aussi requêtes, relations (populate), hooks.
Défi 48h — appliquez demain
Définissez 1 schéma Mongoose avec 2 validations et réalisez un CRUD create + find en await.
- Je distingue document/collection de table/ligne.
- Mon schéma valide required et types.
- Mon CRUD est async avec await.
Questions fréquentes
MongoDB (NoSQL) ou SQL : comment choisir ?
Selon la nature des données. MongoDB convient aux données flexibles et évolutives, aux structures imbriquées proches du JSON, au prototypage rapide et aux données peu relationnelles. Le SQL convient aux données fortement relationnelles (jointures nombreuses), à l'intégrité critique (schéma strict, transactions ACID) et aux requêtes croisées complexes. Ni l'une ni l'autre n'est universellement meilleure : Salim commence en MongoDB pour son catalogue souple et garde le SQL (3.4) pour la facturation liée.
Pourquoi utiliser Mongoose plutôt que MongoDB directement, et faut-il des schémas ?
Mongoose apporte schémas + validation (rejet des données incohérentes avant enregistrement), des modèles et méthodes élégantes (CRUD, filtres, tri, pagination, populate), des hooks (hacher un mot de passe avant save) et une API cohérente. Oui, définissez des schémas même si MongoDB ne l'impose pas : sans eux, champs renommés et types mélangés garantissent le chaos. La souplesse reste (champs optionnels, structures imbriquées), mais sous contrôle.
Que fait le schéma Mongoose pour des données MongoDB ?