API REST avec NodeJS, Express et MongoDB

Aujourd’hui nous allons voir comment développer une API REST pour gérer une TodoList (oui oui, c’est original) avec NodeJS, Express et MongoDB. Le code est disponible ici.

Prérequis

Avant de pouvoir commencer, il vous faudra installer :

Création du projet

Lancez simplement les commandes suivantes dans votre terminal :

mkdir my-first-node-api && cd my-first-node-api
npm init

Votre terminal vous demandera de renseigner des paramètres, vous pouvez laisser ceux par défaut.

Installez maintenant le framework express :

npm install express --save

Votre fichier package.json devrait ressembler à ça :

{
"name": "my-first-node-api",
"version": "1.0.0",
"description": "My first NodeJs REST API",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.15.2"
}
}

Il faut maintenant ajouter le script "start": "node index.js" à la liste de vos scripts dans le fichier package.json :

{
"name": "my-first-node-api",
"version": "1.0.0",
"description": "My first NodeJs REST API",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.15.2"
}
}

Créez maintenant la structure de dossiers et fichiers pour l’application, en lançant les commandes suivantes dans votre terminal :

mkdir "api/route" "api/controller" "api/model"
touch "index.js" "api/route/index.js" "api/controller/todoList.js" "api/model/todoItem.js"

Bon, les windowsiens qui n’ont même pas les commandes bash dans leur terminal, vous pouvez créer les fichiers vides avec : type nul > index.js && type nul > api\route\index.js && type nul > api\controller\todoList.js && type nul > api\model\todoItem.js

Votre structure de fichiers devrait ressembler à ça :

|-- my-first-node-api
|-- index.js
|-- package.json
|-- node_modules
|-- api
|-- controller
| |-- todoList.js
|-- model
| |-- todoItem.js
|-- route
|-- index.js

Commençons à écrire un peu de code dans le fichier index.js:

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.listen(port);

console.log('Your first node api is running on port: ' + port);

Et testons dès maintenant que tout fonctionne, lancez dans votre terminal :

npm start

Vous devriez voir dans ce même terminal : Your first node api is running on port: 3000
Pressez Ctrl+C pour quitter l’application

Ca y est, votre projet fonctionne, passons maintenant aux choses plus intéressantes.

Code, code, code

Installation des dépendances

Installez donc mongoose qui permettra d’intéragir avec la base mongodb et body-parser qui permettra de parser le contenu des requêtes qui sera envoyé à votre API

npm install mongoose body-parser --save

Le modèle

Une fois l’installation terminée, vous allons pouvoir créer votre premier modèle, écrivez ceci dans le fichier api/model/todoItem.js:

const mongoose = require('mongoose'); // Import de la librairie mongoose
const Schema = mongoose.Schema;

// Définition du schéma
const TodoItemSchema = new Schema({
name: {type: String, required: true},
status: {type: String, enum: ['todo', 'inProgress', 'done'], default: 'todo', required: true}
},
{timestamps: true} // Pour avoir les dates de création et de modification automatiquement gérés par mongoose
);

module.exports = TodoItemSchema; // Export du schéma

Le contrôleur

Passons maintenant au controlleur, dans le fichier api/controller/todoList.js :

const todoItem = require('../model/TodoItem'); // On en a déjà parlé, vous vous en rappelez?
const mongoose = require('mongoose'); // Import du schéma
const TodoItem = mongoose.model('TodoItem', todoItem); // Création du modèle à partir du schéma

function respond(err, result, res) { // Fonction utilisée tout au long du contrôleur pour répondre
if (err) {
return res.status(500).json({error: err});
}
return res.json(result);
}

const todoListController = {
getAll: (req, res) => { // Récupérer tous les items de la TodoList
TodoItem.find({}, (err, todoItems) => {
return respond(err, todoItems, res);
});
},
create: (req, res) => { // Créer une tâche
const newTodoItem = new TodoItem(req.body);
newTodoItem.save((err, savedTodoItem) => {
return respond(err, savedTodoItem, res);
});
},
get: (req, res) => { // Récupérer une tâche
TodoItem.findById(req.params.id, (err, todoItem) => {
return respond(err, todoItem, res);
});
},
update: (req, res) => { // Mettre à jour une tâche
TodoItem.findByIdAndUpdate(req.params.id, req.body, (err, todoItem) => {
return respond(err, todoItem, res);
});
},
delete: (req, res) => { // Supprimer une tâche
TodoItem.findByIdAndRemove(req.params.id, (err, todoItem) => {
return respond(err, todoItem, res);
});
}
};

module.exports = todoListController; // Export du contrôleur

Les routes

Enfin, passons au routing, écrivez dans le fichier api\route\index.js :

const todoListController = require('../controller/todoList'); // Import du contrôleur

module.exports = (app) => {
app.route('/todoitems').get(todoListController.getAll);
app.route('/todoitems').post(todoListController.create);
app.route('/todoitems/:id').get(todoListController.get);
app.route('/todoitems/:id').put(todoListController.update);
app.route('/todoitems/:id').delete(todoListController.delete);

app.use((req, res) => { // Middleware pour capturer une requête qui ne match aucune des routes définies plus tôt
res.status(404).json({url: req.originalUrl, error: 'not found'});
});
};

Le modèle, le contrôleur ainsi que les routes sont prêts, il ne reste plus qu’à tout lier, dans le fichier index.js :

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const routes = require('./api/route/index');

const app = express();
const port = process.env.PORT || 3000;

mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/TodoListDB');

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

routes(app);
app.listen(port);

console.log('Your first node api is running on port: ' + port);

Ca y est ! Tout le code est la, plus qu’à lancer et tester !
Lancez mongodb dans un premier terminal :

mongod

Puis, dans un autre terminal, l’application :

npm start

Tests de l’API

Il est temps d’ouvrir Postman ! Entrez dans la barre de requête l’URL : http://localhost:3000/todoitems, dans la liste déroulante du choix de la méthode, choisir GET et terminer par cliquer sur le bouton SEND. La réponse devrait être [] car vous n’avez pour l’instant aucune tâche dans votre base de données !

Postman Get All Items

Créez en une en lançant une requête POST sur la même URL que pour la requête précédente. Dans l’onglet body cochez la case raw puis choisissez JSON à la place de Text. Dans le corps de votre requête, entrez ceci :

{
"name": "Ma première tâche",
"status": "inProgress"
}

Postman Post Item

La réponse devrait ressembler à ceci :

{
"__v": 0,
"updatedAt": "2017-04-27T16:36:18.952Z",
"createdAt": "2017-04-27T16:36:18.952Z",
"name": "Ma première tâche",
"_id": "59021e0269a7620bd053883e",
"status": "inProgress"
}

Testez maintenant la récupération d’une seule tâche, en appelant la route http://localhost:3000/todoitems/id où l’id correspond au champ "_id" de la réponse de la précédente route, en méthode GET. La réponse est la même que la précédente.

Je vous laisse vous débrouiller pour les routes de mise à jour (méthode PUT) et de suppression (méthode DELETE).

Il est possible d’importer ces requêtes dans Postman via cette URL : https://www.getpostman.com/collections/4ca6f4614c93acf9ef42