Practical Programming
API Node JS avec MongoDB

Node JS MongoDB: Connecter votre API à une base de donnée MongoDB

Dans le post 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 vrai 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ée 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.

Une illustration représentant API Rest Node JS MongoDB

Pourquoi utiliser MongoDB avec une API Node JS ?

ℹ️  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

  1. Une compétence très demandée
    Cette demande est probablement née d’une tendance autour de la stack MEAN ou MERN. Toujours est-il qu’aujourd’hui de nombreux projets en entreprise utilisent la stack Node JS et MongoDB.
  2. Une solution pratique
    MongoDB fonctionne en stockant vos données sous formes d’objets BSON, très similaire à l’objet JSON avec lesquels les développeurs JavaScript sont très familiers.

Les choix techniques avant de démarrer

MongoDB en local ou dans le cloud ?

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é ⚠️

Quelle librairie utiliser pour connecter MongoDB à notre API Node JS?

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êt de votre base de données et utiliser les fonctions natives de MongoDB comme si vous étiez connecté 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.

Connexion de MongoDB à notre API Node JS

⏱️ 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.

Evaluation des connexions sur notre 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.

Lancement de notre API Node JS MongoDB

A en croire mon terminal, la callback de ma fonction MongoClient.connect() a bien été appelée, ce qui me laisse croire que la connexion à la base de donnée a bien fonctionné. Je vais vérifier à nouveau sur le shell:

On observe que notre API Node JS MongoDB a bien réussi à se connecter à la base de données

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.

Importer nos données dans MongoDB

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
Importation des données du fichier JSON vers MongoDB

Vérifions maintenant que les données sont bien dans notre base de données via le shell:

Nous observons que notre base de données contient bien les données du fichier JSON que nous venons d'importer.
L’ensemble des données du fichier JSON a bien été intégré dans la base de données mongo

Refactoriser le code de l’API Node JS MongoDB

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.

Récupérer les données de la base MongoDB

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ée 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ée 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é 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é 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
    }
})

Les nuances de l’update

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:

  • La modification, où vous venez modifier uniquement un ou plusieurs champs de votre objet
  • Le remplacement; où vous écrasez l’intégralité des données de l’objet par les nouvelles données

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

A vous de jouer !

A 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 correspondants 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ée est aussi conçu avec une callback. Essaye de le refactorer de tel sorte qu’il utilise une Promise au lieu d’une callback.

Besoin d’aide pour avancer ?

Je ne vois pas comment terminer l’exercice ?

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 là 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.

Pourquoi est-ce qu’on a “id” et “_id” pour chaque objet parking?

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:

  • Ignorer le champ _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
  • Remplacer le champ id par le champs _id, cette solution est la plus simple mais vous allez vous retrouver avec des URL peu lisibles comme /parkings/5f48a58c9f93aa6fe9c865cc
  • Créer un champ 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 champs nom dans la méthode appelée lors de la création d’un nouveau parking.

Retrouvez le code source de l’API sur Github

Tu pourra retrouver le code source de notre API Node JS MongoDB si tu souhaites comparer ton code au miens.
Si tu as le repository sur ton poste, n’oublie pas de changer de branche pour aller sur la branche part2/mongodb

La suite de l’API NodeJS 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 !

Retrouvez nous

N'hésitez pas à nous suivre sur les différents réseaux sociaux !

Most popular

Most discussed

Share This