Installation
Pour installer mongoose dans un projet Node.js : npm install mongoose
.
Il peut alors être importé dans l'application :
// Importation du module mongoose
const mongoose = require('mongoose');
Connexion à MongoDB
La connexion à un serveur et une DB MongoDB est réalisé grâce à mongoose.connect
.
La méthode accepte différents paramètres et options : cf. la
documentation officielle.
Un schéma de connection "classique" utilise l'appel suivant :
mongoose.connect('mongodb://username:password@host:port/database')
avec :
-
username
: le nom de l'utilisateur MongoDB (ex. 'root').
Il s'agit du nom d'utilisateur que vous avez noté lors de la
création de cluster.
-
password
: le mot de passe de l'utilisateur MongoDB (ex. 'unmdpcompliqué')
Il s'agit du mot de passe que vous avez noté lors de la
création de cluster.
-
host
: le nom de la machine où tourne le serveur MongoDB (ex. '127.0.0.1').
-
port
: le port de la machine où tourne le serveur MongoDB (ex. '27017').
-
database
: le nom de la base de données où se trouvent les collections visées
(ex. 'movies_db').
Connexion sur un serveur personnel
Par défaut, un serveur MongoDB ne gère pas les contrôles d'accès, ce qui signifie qu'il n'y a pas
de user
et password
à donner pour s'y connecter.
Bien entendu, utiliser un serveur MongoDB sans contrôle d'accès n'est viable que dans le cadre
du développement et de tests !
Si vous avez installé votre propre version de MongoDB en local,
le code de connexion à utiliser est :
Code de connexion à un MongoDB Personnel
Connexion à Atlas
Si votre serveur est hébergé dans le cloud MongoDB Atlas, la construction
de la chaine de connexion un peu plus complexe que pour un serveur local.
Heureusement, la partie Database
d'Atlas propose un bouton
Connect
pour récupérer le pattern de connexion selon la méthode choisie :
sélectionnez Connect your applicaption
.
Vous pouvez alors copier le pattern de connexion au cluster...
... et l'utiliser dans app.js
.
Code de connexion à MongoDB Atlas
Astuces :
Ce code étant assez volumineux, personnellement, je préfère le mettre dans un fichier (par exemple)
nommé mongoose_init.js
que je range dans le dossier controllers
et que
j'importe dans app.js
grâce à un simple require("./controllers/mongoose_init")
.
De plus, les information de connexion user
et pwd
étant très sensibles,
je préfère les externaliser dans un fichier bien rangé dans un dossier que je ne vais pas partager
(par exemple en donnant une correction à mes étudiants :D).
J'ai défini ces valeurs dans des variables d'environnement (dans le fichier .env
):
(cf. cours sur dotenv)
(dans le fichier .env
)
MONGO_HOST='hote hebergeant le serveur MongoDB'
MONGO_USER='nom utilisateur'
MONGO_PWD='mot de passe compliqué'
Je récupère ensuite ces informations dans les variables d'environnement :
(dans le fichier qui effectue la connexion)
const host = process.env.MONGO_HOST ;
const user = process.env.MONGO_USER ;
const pwd = process.env.MONGO_PWD ;
const db_name = 'movies_db' ;
const mongoDB = `mongodb+srv://${user}:${pwd}@${host}/${db_name}?retryWrites=true&w=majority` ;
mongoose.connect(mongoDB, { useUnifiedTopology: true })
.then(() => console.log('MongoDB OK !'))
.catch(() => console.log('MongoDB ERREUR !'));
Schéma & Model mongoose
Mongoose utilise une approche Objet pour mapper les documents MongoDB.
Le mapping est effectué en créant des instances de la classe
mongoose.Model
grâce à la classe
mongoose.Schema
.
Les modèles créés sont par convention mis dans le dossier models
du projet.
La collection movies
que nous avons
créée dans la DB movies_db
contient comme son nom l'indique des documents qui représentent des films.
Nous allons donc créer le modèle Movie
dans le fichier Movies.js
.
Ce schéma/modèle indique en particulier que :
-
Le champ
title
doit être une chaîne (String
).
Il est requis (required: true
), ce qui signifie qu'on ne peut pas créer de
Movie
sans lui donner un title
.
-
Le champ
synopsis
doit être une chaîne (String
).
Il n'est pas requis et, s'il n'est pas fourni, il prendra la valeur indiquée pour default
.
Requêtes mongoose
Les modèles
(mongoose.Model
)
possèdent diverses méthodes pour lancer des requêtes
(mongoose.Query
)
plus ou moins complexes.
La liste et les options sont exhaustives : nous n'allons pas toutes les étudier ici.
Utilisez la documentation
de
mongoose.Model
et
mongoose.Query
en fonction de vos besoins.
Ces méthodes renvoient des
Promise
Javascript.
On peut donc les utiliser en ansynchrone avec un .then(....).catch(....)
, ou en synchrone
(mode bloquant) avec await
.
Pour exemplifier, nous allons utiliser la méthode
find
en asynchrone pour récupérer tous les documents correspondant à un sélecteur
(NB : si aucun sélecteur n'est fourni,
find
renvoie la totalité des documents).
Le fichier moviesController.js
ci-dessous est utilisé dans l'application Express pour traiter
les requêtes sur la route /movies
en renvoyant la vue moviesList
.
Vous remarquerez aussi qu'on peut enchainer les traitements des
requêtes mongoose.
Par exemple, le code ci-dessous chaine le
find
avec un
sort
pour trier les données.
Exercice 11
Créez l'application et les fichiers cités ci-avant pour obtenir le site ci-dessous avec en
particulier la route /movies
qui affiche la liste des titres de films de la collection
movies
.
La liste des films doit être récupérée dynamiquement dans la DB (!).
NB : l'exemple ci-dessous se connecte à mon cluster MongoDB Atlas
qui possède plusieurs documents/films
dans sa collection /movies
.
ATTENTION : LA DÉMO CI-DESSOUS N'EST VISIBLE QUE SI VOUS UTILISEZ LES ORDINATEURS DES
SALLES DE TP À L'ULCO (le serveur tourne sur réseau local) !!!
Au cas où vous n'y avez pas accès, voici les copies d'écran :
C.R.U.D.
L'acronyme C.R.U.D. signifie Create / Read / Update / Delete.
C'est tout ce que nous allons apprendre à faire avec nos documents MongoDB.
Create
La création de document est réalisée grâce à méthode
create
de la classe
mongoose.Model
.
Ex. Voici comment créer un Movie
et le sauvegarder dans sa collection :
Read
La lecture est réalisée grâce aux méthodes
find
,
findOne
,
ou encore
findById
.
Ex. Voici comment récupérer le 1er Movie
de la collection
qui possède un title
avec la valeur 'Terminator'
avec findOne
:
Update
La mise à jour est réalisée grâce aux méthodes
update
,
updateOne
,
findByIdAndUpdate
ou encore
findOneAndUpdate
.
On peut aussi mettre à jour plusieurs documents en même temps grâce à
updateMany
.
Ex. Voici comment modifier un Movie
avec un _id
spécifique
avec findByIdAndUpdate
:
Delete
La suppression de document est réalisée grâce aux méthodes
findByIdAndDelete
,
findOneAndDelete
,
ou encore
deleteOne
.
On peut aussi supprimer plusieurs documents en même temps grâce à
deleteMany
.
Ex. Voici comment supprimer un Movie
avec un titre
spécifique
avec findOneAndDelete
:
Variantes des méthodes vues
Les méthodes que nous venons de voir pour le C.R.U.D. sont des méthodes de classe
appelées directement sur un mongoose.Model
: dans nos exemples, les méthodes sont appelées
sur Movie
.
La plupart des méthodes que nous venons de voir pour le C.R.U.D. possèdent un équivalent à appeler
sur une instance de Model
.
Par exemple, la méthode
Model.create
que nous avons vue est quelque peu équivalent à la méthode
Model.prototype.save
qui doit être appelée non plus sur Movie
, mais sur une de ses instances.
Ces méthodes sont celles qui ont le préfixe Model.prototype
l'API mongoose.Model
.
Voici l'équivalent de
notre exemple
pour Create en utilisant Model.prototype.save
:
Exercice 12
Pour tester les exemples de code vus ci-avant, créez un serveur/application avec un
ensemble de routes GET dédiées à un exemple de chaque action CRUD.
Vous aurez ainsi les routes :
-
/
: effectue une redirection vers la route /movies
(ci-dessous).
-
/movies
: affiche la liste des films (title + synopsis) sous forme de tableau dans une vue EJS.
-
/movies/create
: crée un document avec le title
'House' et ré-affiche la liste
(dans laquelle on doit donc voir le nouveau film).
-
/movies/read
: affiche les informations (title + synopsis) du film 'House' dans une vue EJS.
-
/movies/update
: modifie les informations du film 'House' et re-affiche la liste.
(Pour simplifier, utilisez un findOneAndUpdate
à la place du findByIdAndUpdate
de l'exemple).
-
/movies/delete
: supprime le film 'House' de la collection et et re-affiche la liste
(dans laquelle le film doit donc avoir disparu).
Exemple après un /movies/create
puis un /movies/update
:
Pour les curieux :
House
:).
Le sujet se trouve
ICI.