MongoDB est une technologie de base de données NoSQL qui est très populaire. Dans ce post, nous allons reprendre l’exemple de code précédent et connecter une base de données MongoDB à notre API NodeJS.
Dans le poste précédent, nous avons vu comment créer une Node JS API en simulant des données depuis des fichiers JSON. Or dans la vraie vie, une API va être connectée à une (voir plusieurs) base de données afin de pouvoir faire son travail.
MongoDB est une technologie de base de données NoSQL qui est très populaire dans l’écosystème JavaScript. Cette compétence est souvent recherchée chez les développeurs NodeJS. Dans ce post, nous allons reprendre l’exemple de code précédent et connecter une base de données MongoDB à notre API NodeJS.
🧑🎓 Vous souhaitez apprendre à utiliser NodeJS 📖 ?
Practical Node vous enseigne les bonnes pratiques NodeJS pour être opérationnel dès le premier jour !
ℹ️ Une API Node JS est capable de s’interfacer avec de très nombreuses technologies de base de données. MongoDB n’est en aucun cas le meilleur choix dès lors que vous concevez une API Node JS. Je vous invite à consulter notre guide complet sur MongoDB pour comprendre ses forces et ses faiblesses.
Comme vous le savez peut-être, il existe deux façons d’avoir accès à une base de données MongoDB Soit en l’installant localement sur votre PC ou votre mac, soit en utilisant un service cloud comme MongoDB Atlas.
Dans notre exemple, nous allons utiliser une base de données en local.
⚠️ Assurez-vous de l’avoir correctement installé ⚠️
Pour pouvoir vous connecter à votre base de données MongoDB, là aussi, deux choix s’offrent à vous. Utiliser la librairie officielle de MongoDB ou utiliser la librairie Mongoose.
MongoDB Client vous permet d’être au plus près de votre base de données et utiliser les fonctions natives de MongoDB comme si vous étiez connectés sur un shell Mongo.
Mongoose est une surcouche qui vient apporter la possibilité d’imposer un Schema, soit une structure obligatoire, à votre modèle de données. C’est également l’option la plus populaire.
Dans notre cas nous allons utiliser Mongodb Client et l’installer via le gestionnaire de paquets npm.
⏱️ Si vous n’avez pas suivi le tutoriel précédent, vous pouvez retrouver le code source ici. Clonez le repository sur votre machine pour reprendre où nous en étions.
La première étape est d’ajouter le package mongodb à notre projet. Je ne vous explique pas la commande (mais n’oubliez pas de vous mettre dans le répertoire du projet dans votre terminal).
npm install mongodb
Suivant la documentation de Mongodb Client, voici à quoi ressemblerait la connexion à notre base de données.
const express = require('express')const app = express()const parkings = require('./parkings.json')/** * Import MongoClient & connexion à la DB */const MongoClient = require('mongodb').MongoClient;const url = 'mongodb://localhost:27017';const dbName = 'parkingApi';let db MongoClient.connect(url, function(err, client) { console.log("Connected successfully to server"); db = client.db(dbName);});app.use(express.json())app.get('/parkings', (req,res) => { res.status(200).json(parkings)})app.get('/parkings/:id', (req,res) => { const id = parseInt(req.params.id) const parking = parkings.find(parking => parking.id === id) res.status(200).json(parking)})app.post('/parkings', (req,res) => { parkings.push(req.body) res.status(200).json(parkings)})app.put('/parkings/:id', (req,res) => { const id = parseInt(req.params.id) let parking = parkings.find(parking => parking.id === id) parking.name =req.body.name, parking.city =req.body.city, parking.type =req.body.type, res.status(200).json(parking)})app.delete('/parkings/:id', (req,res) => { const id = parseInt(req.params.id) let parking = parkings.find(parking => parking.id === id) parkings.splice(parkings.indexOf(parking),1) res.status(200).json(parkings)})app.listen(8080, () => { console.log("Serveur à l'écoute")})
Avant de relancer mon serveur, je vais me connecter au Shell Mongo et évaluer les connexions actives au serveur MongoDB.
Je vois dans l’objet current
que pour l’instant il n’y a qu’une seule connexion en cours. Je vais maintenant lancer mon serveur Node et vérifier à nouveau le nombre de connexion.
À en croire mon terminal, la callback de ma fonction MongoClient.connect()
a bien été appelé, ce qui me laisse croire que la connexion à la base de données a bien fonctionné. Je vais vérifier à nouveau sur le shell:
On voit que cette fois il y a 2 connexions courantes, celle du Shell et celle de l’API. On peut être confiant sur le fait que notre API est bien connectée à la base de données.
⚠️ Attention toutefois au message de la callback « Connected successfully to server » car il peut être trompeur. Si je cherche à provoquer une erreur de connexion en modifiant l’URL de connexion à mongodb://localhost:21
au lieu de mongodb://localhost:27017
, mon terminal va clairement m’alerter comme quoi il y a une erreur, mais il va passer dans la callback et exécuter l’instruction console.log("Connected successfully to server");
alors que la connexion n’est pas établie.
Jusque-là, nous utilisions des données dans un fichier JSON plutôt qu’une base de données. Pour continuer notre cas pratique, nous allons maintenant importer ces données dans MongoDB.
Pour vous simplifier la tâche, vous pouvez utiliser un client graphique MongoDB tel que Mongo Compass ou si vous êtes à l’aise avec les requêtes mongo, utiliser le shell directement. C’est l’option que nous allons choisir ici
mongoimport --jsonArray --db parkingApi --collection parkings --file parkings.json
Vérifions maintenant que les données sont bien dans notre base de données via le shell:
Nous avons la certitude que notre API se connecte bien à notre base de données et que celle-ci contient les éléments pour que notre API continue de fonctionner. Nous allons maintenant réécrire le code de notre API Node JS MongoDB pour utiliser cette nouvelle base de données.
Comme on l’a vu dans notre requête dans le shell, la requête suivante va nous permettre d’afficher la liste de tous nos parkings:
db.parkings.find()
Je vais donc adapter le code correspondant à ma route GET / afin d’exécuter cette requête:
app.get('/parkings', (req,res) => { db.collection('parkings').find({}).toArray(function(err, docs) { if (err) { console.log(err) throw err } res.status(200).json(docs) }) })
Ce code vient de la documentation de Mongodb. Il reprend l’objet db qui correspond à la connexion à la base de données Mongo et nous permet d’utiliser ses requêtes. Dans notre cas, nous avons utilisé la requête find
sans passer de filtres de telle sorte à ce qu’il nous retourne tous les objets présents en base.
⚠️ Le code sollicitant une opération en base de données est asynchrone
Dans la mesure où une requête interrogeant une base de données va prendre plus que quelques millisecondes, l’event loop NodeJS va sous-traiter cette opération dans une queue à part. Lorsque la base de données aura répondu à la requête, au prochain passage de la boucle d’évènement Node JS, exécutera la fonction en callback, qui contient l’ordre de réponse.
L’utilisation des callbacks n’est plus très appréciée dans l’écosystème JavaScript. Depuis l’arrivée de l’ES6, les promises sont privilégiées. Modifions notre code afin qu’il soit plus « acceptable »
app.get('/parkings', (req,res) => { db.collection('parkings').find({}).toArray() .then(docs => res.status(200).json(docs)) .catch(err => { console.log(err) throw err })})
Depuis l’ES8, une troisième syntaxe est proposée pour faire ses requêtes asynchrones. La syntaxe async/await
offre une syntaxe très lisible pour les requêtes asynchrones. Voici à quoi ça ressemble:
app.get('/parkings', async (req,res) => { try { const docs = await db.collection('parkings').find({}).toArray() res.status(200).json(docs) } catch (err) { console.log(err) throw err }})
ℹ️ La syntaxe async/await est simplement du sucre syntaxique autour de l’utilisation des Promises. Les deux pratiques sont retrouvées en entreprise et il est important de savoir les manipuler.
Récupérer un seul parking depuis notre API Node JS MongoDB
Nous avons pu récupérer l’ensemble des parkings, maintenant nous pouvons utiliser la même requête et la modifier légèrement pour ne récupérer qu’un seul objet de la base de données.
app.get('/parkings/:id', async (req,res) => { const id = parseInt(req.params.id) try { const docs = await db.collection('parkings').find({id}).toArray() res.status(200).json(docs) } catch (err) { console.log(err) throw err }})
Dans l’exercice précédent, nous avons très brièvement abordé le sujet de la modification d’un objet dans notre API. Nous utilisions la méthode PUT pour venir remplacer l’intégralité de l’objet à chaque fois.
Or il existe deux approches à la mise à jour d’un objet dans une API CRUD:
MongoDB reproduit ces 2 approches en vous offrant deux méthodes, updateOne et replaceOne, pour répondre à ces besoins.
Parfois vous n’aurez pas l’intégralité des données de votre objet lorsque vous souhaiterez le mettre à jour. Dans ce cas il faudra utiliser la méthode PATCH au lieu de PUT.
Ajoutez une route PATCH /parkings/:id
à votre API
À la différence du guide précédent, celui-ci va être volontairement moins exhaustif. Le but étant de vous faire manipuler MongoDB et vous laisser avancer jusqu’à trouver comment réaliser vos premières opérations MongoDB.
Si vous ne l’avez pas déjà lu, je vous invite à consulter la page décrivant les requêtes CRUD en mongodb dont vous aurez besoin pour terminer ce cas pratique.
Pour terminer votre API NodeJS MongoDB, vous devez modifier le code correspondant aux routes POST /parkings
, PUT /parkings/:id
et DELETE /parkings/:id
pour qu’il interagisse avec votre base de données MongoDB.
Si vous voulez aller plus loin, vous pouvez importer le fichier reservations.json
dans votre base de données, dans une collection reservations et modifier le code correspondant aux requêtes de réservations pour avoir une API NodeJS MongoDB complète.
Une fois que vous aurez terminé, vous pourrez effacer les fichiers parkings.json
et reservations.json
du repository.
🎁 BONUS
Si tu regardes bien, tu verras que le code de connexion à la base de données est aussi conçu avec une callback. Essaye de le refactorer de telle sorte qu’il utilise une Promise au lieu d’une callback.
Les 3 fonctionnalités restantes sont l’ajout d’un nouveau parking, la modification d’un parking existant et la suppression d’un parking.
Je te suggère de commencer par l’ajout d’un nouveau parking. Regarde dans la page des query mongo comment ajouter un élément à la base de données. Inspire-toi du code présent dans les routes GET et modifie la requête mongoDB pour qu’elle fasse ce que tu veux.
Le code pour effacer un parking est également très similaire à celui de l’ajout d’un parking. Trouve la bonne méthode dans la doc de mongoDB et applique-la dans la bonne route.
Enfin, les deux méthodes de modifications (PUT et PATCH) sont un petit peu plus complexes. Là aussi tu trouveras facilement des exemples sur la documentation.
Dans notre premier guide, nous avions besoin d’un id
pour pouvoir identifier et manipuler chaque objet parking indépendamment. C’est pourquoi je l’ai ajouté en dur dans notre objet JSON.
Lors de l’import de notre fichier JSON, MongoDB a créé par défaut _id
, rendant le champ id
redondant. Pour continuer votre API Node JS MongoDB, trois options s’offrent à vous:
_id
et continuer à créer manuellement le champ id
,en tant que nombre qui s’incrémente pour chaque objet. Vous pourriez créer une fonction qui viendrait automatiquement reprendre et incrémenter l’id/parkings/5f48a58c9f93aa6fe9c865cc
slug
qui serait une chaîne de caractères unique reprenant le nom de votre parking, tout en minuscule et sans espaces. Par exemple : « parking-pas-cher-roissy » ou « parking-vinci-lille-europe ». L’URL est bien plus lisible mais risque de devenir plus longue. Dans ce cas, il faudra prévoir la création de ce slug à partir du champ nom dans la méthode appelée lors de la création d’un nouveau parking.Tu pourras retrouver le code source de notre API Node JS MongoDB si tu souhaites comparer ton code au mien.
Si tu as le repository sur ton poste, n’oublie pas de changer de branche pour aller sur la branche part2/mongodb
Cette série de mise en pratique ne s’arrêtera pas là ! Au programme du prochain épisode on va revoir l’architecture de notre API et mettre en place le pattern d’architecture N-tier !
Les maladies inflammatoires chroniques de l’intestin ou "MICI" sont invisibles, mais leurs impacts sur la…
Depuis l'été, j'ai un Pixel qui intègre à la fois un TPU (Tensor Processing Unit)…
On se retrouve dans un nouvel article avec toutes les infos sur cette nouvelle saison…
Pourquoi l’inclusion numérique est essentielle : le point avec Mathieu Froidure. Dans un monde de…
Elles sont passées où les femmes dans la tech ? Entre le manque de représentation…