Table des matières

Nous allons créer une application de contacts qui expose une API REST. Une fiche contact contient un nom et un age. Les données seront sauvegardées dans une base de données mongodb.

Environnement au CREMI

Les binaires par défaut de node sont un peu trop anciens. Nous allons utiliser ceux qui se trouvent dans /opt/users/bin. Pour se faire, dans un terminal, entrez la commande suivante :

$ export PATH=/opt/users/bin:$PATH

Modifiez ensuite votre fichier de configuration ~/.bashrc ou équivalent pour que la modification soit pérenne.

Mise en place du projet

1) Créez la structure du projet en entrant les lignes suivantes dans un terminal

npm init -y
npm install --save express mongoose
npm install --save-dev dotenv nodemon

Dans cette séance, nous allons utiliser nodemon, une version plus légère de pm2. Pour que notre application soit plus portable, nous allons positionner des variables d’environnement au lieu de constantes en dur dans le code. Pour simplifier le développement, nous utiliserons le module dotenv.

2) Créez un fichier .env contenant le texte suivant :

MONGO_URI=mongodb://127.0.0.1:27017
DB_NAME=m2web

3) Modifiez le fichier package.json ainsi

"scripts": {
    "start": "node src/server.js",
    "dev": "nodemon src/server.js"
},

4) Nous allons mettre en place une arborescence de fichiers pour mieux séparer les différentes parties de notre application

mkdir client
mkdir src src/config src/controllers src/models src/routes

Commençons à construire l’API

Créez le fichier server.js dans le répertoire src comme suit :

const express = require('express');

const app = express();

app.use(express.json());

app.get('/', (req, res) => {
  res.send('Hello World with Express')
});

app.listen(process.env.PORT || 5000, () => console.log('Up and running 🚀'));

Afin de démarrer l’application, exécutez la commande suivante :

npm run dev

puis ouvrez l’url http://localhost:5000 dans un navigateur.

Base de données

Sur les machines du CREMI, il faut démarrer un serveur de base de données en local. Exécutez les commandes suivantes :

$ mkdir ~/espaces/travail/db
$ mongod --dbpath ~/espaces/travail/db

Utilisation de Mongo en JS

Dans cette séance, nous allons utiliser un modèle de données pour notre base. Pour ce faire, nous utiliserons le pilote mongoose. La première chose à faire est de définir le schéma des données contenues dans la base. Éditez un fichier contactModel.js dans le répertoire models contenant le texte ci-dessous :

const mongoose = require('mongoose');

// Setup schema
const contactSchema = new mongoose.Schema({
    name: { type: String },
    age:  { type: Number }
});

module.exports = mongoose.model('Contact', contactSchema);

Une bonne pratique est de regrouper toutes les informations de connexion à la base au même endroit. Créez un fichier db.js dans le répertoire config qui contiendra toutes les informations pour se connecter à la base de données.

const mongoose = require('mongoose');
const dotenv = require("dotenv");
dotenv.config();

async function connectDB() {
    try {
        console.log("Opening connection")
         const conn = await mongoose.connect(process.env.MONGO_URI + '/' + process.env.DB_NAME, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
        });
        console.log(`MongoDB Connected: ${conn.connection.host}`);
    } catch(err) { 
        mongoose.disconnect();
        console.error(err);
        process.exit(1);
    }
}

module.exports = connectDB

Modifiez le fichier server.js de la manière suivante

const connectDb = require("./config/db");

const app = express();
connectDb();

Routes

Nous allons définir toutes les routes de notre API dans un fichier index.js dans le dossier routes.

1) Éditez le fichier routes/index.js comme suit:

const router = require('express').Router();

router.get('/', function (req, res) {
    res.status(200).json({
        status: 'API is Working',
        message: 'Welcome!',
    });
});

2) Ajoutez la ligne app.use('/api', routes); dans le fichier server.js

3) Créez un fichier rest.http dans le dossier client contenant les lignes ci-dessous

### Default URL
GET http://localhost:5000/

### Default API URL
GET http://localhost:5000/api/

4) Utilisez le plugin REST Client dans VScode pour tester vos routes. Vous pouvez également utiliser un outil comme curl ou une application comme postman.

Logique métier

1) Créez un fichier contactController dans le répertoire controllers. Nous allons implémenter les méthodes suivantes : getAllContacts, getContactById, updateContact, removeContact, addContact.

const Contact = require('../models/contactModel');

// Get all contacts
module.exports.getAllContacts = async function() {
    let total = await Contact.countDocuments({});
    let limit = parseInt(total);

    try {
        const contacts = await Contact.find().limit(limit);
        return {
            success: true,
            data: contacts,
            total: total.toString(),
        }
    } catch (err) {
        return { success: false, message: "Contacts not found " + err };
    }
}

// Get contact by Id
module.exports.getContactById = async function(id) {
  // todo
}

// Add a new contact, returns the added contact
module.exports.addContact = async function(body) {
  // todo
}
 
// Update an existing contact
module.exports.updateContact = async function(id, name = null, age = null) {
   // todo
}
    
// Remove an existing contact
module.exports.removeContact = async function(id) {
  // todo
}

2) Ajoutons maintenant la route correspondante dans le dossier routes.

const { getAllContacts, getContactById, updateContact, removeContact, addContact } = require('../controllers/contactController');

router.route('/contacts').get(async (req, res) => {
    let response = await getAllContacts();
    if (response.success == true) {
        res.status(200).json(response);
    } else {
        res.status(404).json(response);
    }
});

3) Testez votre nouvelle route (ajoutez une requête dans le fichier rest.http)

4) Ajoutez le code nécessaire pour les routes suivantes

GET /contacts/:id     (getContactById)
POST /contacts        (addContact)
PUT /contacts/:id     (updateContact)
DELETE /contacts/:id  (removeContact)

OpenAPI

Nous allons fournir une documentation de notre API sous la forme d’une spécification openAPI qui permet de décrire précisément les routes d’une API et comment les utiliser.

1) Ajoutez les dépendances suivantes au fichier server.js

const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const options = {
    definition: {
        openapi: '3.0.0',
        info: {
            title: 'Contact REST API',
            description: "A REST API built with Express and MongoDB.",
            version: '0.1',
        },
        servers: [
            {
                url: 'http://localhost:5000/api',
                description: 'Development server',
            },
        ],
    },
    apis: ["./src/routes/*.js"],
}

const openapiSpecification = swaggerJsDoc(options);
app.use('/docs', swaggerUi.serve, swaggerUi.setup(openapiSpecification));

2) Il suffit maintenant d’ajouter quelques annotations dans le fichier routes/index.js de la manière suivante :

/**
 * @openapi
 * /contacts:
 *   get:
 *     description: All contacts
 *     responses:
 *       '200':
 *         description: Returns all the contacts
 */
router.route('/contacts').get(async (req, res) => { [...] }

3) Vous pouvez ensuite visualiser la documentation de l’API via l’url http://localhost:5000/docs