Créer une API REST : bonnes pratiques
Pourquoi créer une API REST : bonnes pratiques ?
Contexte réel : En tant que développeur, vous avez souvent besoin de partager des données entre différents systèmes ou applications. Une API REST est l'outil parfait pour ce faire car elle permet aux différentes parties d'une application de communiquer efficacement.
Un cas d'utilisation concret en 2-3 phrases : Imaginez une application mobile qui a besoin d'afficher les dernières actualités publiées sur un site web. En créant une API REST, le mobile peut simplement faire des appels HTTP pour récupérer les données nécessaires sans avoir à recharger l'application.
Prérequis
- Connaissance de base du JavaScript et du développement web
- Familiarité avec les concepts de backend (Node.js, Express)
- Compétences en gestion de paquets (npm, yarn)
- Brevet d'imagination et de motivation pour réaliser un projet complet
Outils à installer :
- Node.js v14 ou plus récent (https://nodejs.org/)
- npm (Node Package Manager) qui est généralement installé avec Node.js
- Un éditeur de code, comme Visual Studio Code (https://code.visualstudio.com/)
Concepts fondamentaux
1. Méthodes HTTP
La méthode HTTP détermine l'action à effectuer sur la ressource identifiée par le chemin d'accès.
GET : Récupère les données de la ressource.
// Exemple : GET /api/users
app.get('/api/users', (req, res) => {
// Code pour récupérer et envoyer les utilisateurs
});
POST : Crée une nouvelle ressource.
// Exemple : POST /api/users
app.post('/api/users', (req, res) => {
// Code pour créer un nouvel utilisateur
});
PUT : Met à jour une ressource existante.
// Exemple : PUT /api/users/1
app.put('/api/users/1', (req, res) => {
// Code pour mettre à jour l'utilisateur avec id = 1
});
DELETE : Supprime une ressource existante.
// Exemple : DELETE /api/users/1
app.delete('/api/users/1', (req, res) => {
// Code pour supprimer l'utilisateur avec id = 1
});
2. Structures de données
La structure des données détermine comment les données seront stockées et manipulées.
Modèle Objet Relational (MOR) :
// Exemple d'un modèle utilisateur en MongoDB
const UserSchema = new mongoose.Schema({
name: String,
email: String,
password: String
});
const User = mongoose.model('User', UserSchema);
3. Contrôleurs et routes
Les contrôleurs gèrent les requêtes HTTP et renvoient une réponse.
Contrôleur de gestion des utilisateurs :
// Exemple d'un contrôleur pour la ressource /api/users
const UserController = {
getUsers: async (req, res) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la récupération des utilisateurs' });
}
},
createUser: async (req, res) => {
try {
const newUser = await User.create(req.body);
res.status(201).json(newUser);
} catch (error) {
res.status(400).json({ error: 'Erreur lors de la création de l'utilisateur' });
}
}
};
module.exports = UserController;
Définition des routes :
// Exemple d'association des contrôleurs aux routes
app.get('/api/users', UserController.getUsers);
app.post('/api/users', UserController.createUser);
Mise en pratique : projet fil rouge
Étape 1 : Initialiser le projet
Créez un nouveau dossier pour votre projet et initialisez-le avec npm :
mkdir api-rest-todo-list
cd api-rest-todo-list
npm init -y
Installation des dépendances :
npm install express mongoose body-parser cors
Étape 2 : Configurer le serveur
Créez un fichier server.js :
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
app.use(bodyParser.json());
app.use(cors());
// Import des contrôleurs et routes
const todoController = require('./controllers/todoController');
app.use('/api/todos', todoController);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur en cours sur le port ${PORT}`);
});
Étape 3 : Créer le modèle de données
Créez un fichier models/todo.js :
// models/todo.js
const mongoose = require('mongoose');
const TodoSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: String,
completed: {
type: Boolean,
default: false
}
});
module.exports = mongoose.model('Todo', TodoSchema);
Étape 4 : Créer le contrôleur
Créez un fichier controllers/todoController.js :
// controllers/todoController.js
const Todo = require('../models/todo');
exports.getTodos = async (req, res) => {
try {
const todos = await Todo.find();
res.status(200).json(todos);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la récupération des tâches' });
}
};
exports.createTodo = async (req, res) => {
try {
const newTodo = await Todo.create(req.body);
res.status(201).json(newTodo);
} catch (error) {
res.status(400).json({ error: 'Erreur lors de la création de la tâche' });
}
};
exports.updateTodo = async (req, res) => {
try {
const updatedTodo = await Todo.findByIdAndUpdate(req.params.id, req.body, { new: true });
if (!updatedTodo) return res.status(404).json({ error: 'Tâche non trouvée' });
res.status(200).json(updatedTodo);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la mise à jour de la tâche' });
}
};
exports.deleteTodo = async (req, res) => {
try {
const deletedTodo = await Todo.findByIdAndDelete(req.params.id);
if (!deletedTodo) return res.status(404).json({ error: 'Tâche non trouvée' });
res.status(204).send();
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la suppression de la tâche' });
}
};
Étape 5 : Définir les routes
Ajoutez les routes dans server.js :
// server.js
const todoController = require('./controllers/todoController');
app.get('/api/todos', todoController.getTodos);
app.post('/api/todos', todoController.createTodo);
app.put('/api/todos/:id', todoController.updateTodo);
app.delete('/api/todos/:id', todoController.deleteTodo);
Étape 6 : Tester l'API
Lancez le serveur :
node server.js
Utilisez Postman ou curl pour tester les routes :
## GET /api/todos
curl -X GET http://localhost:3000/api/todos
## POST /api/todos
curl -X POST http://localhost:3000/api/todos -H "Content-Type: application/json" -d '{"title": "Tâche 1", "description": "Description de la tâche 1"}'
Erreurs fréquentes et debugging
1. TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array-like object. Received undefined
Code incorrect :
// db.js
const mongoose = require('mongoose');
mongoose.connect();
Code correct :
// db.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/todo-app', { useNewUrlParser: true, useUnifiedTopology: true });
2. SyntaxError: Unexpected token 'export'
Code incorrect :
// models/todo.js
module.exports = (mongoose) => {
const TodoSchema = new mongoose.Schema({
title: String,
description: String
});
return mongoose.model('Todo', TodoSchema);
};
Code correct :
// models/todo.js
const mongoose = require('mongoose');
const TodoSchema = new mongoose.Schema({
title: String,
description: String
});
module.exports = mongoose.model('Todo', TodoSchema);
3. Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
Code incorrect :
// controllers/todoController.js
exports.getTodos = async (req, res) => {
try {
const todos = await Todo.find();
res.status(200).json(todos);
res.status(500).json({ error: 'Erreur lors de la récupération des tâches' });
} catch (error) {}
};
Code correct :
// controllers/todoController.js
exports.getTodos = async (req, res) => {
try {
const todos = await Todo.find();
res.status(200).json(todos);
} catch (error) {
res.status(500).json({ error: 'Erreur lors de la récupération des tâches' });
}
};
Pour aller plus loin
- Authentification et sécurité : Apprenez à sécuriser votre API en utilisant JWT (JSON Web Tokens).
- Documentation : Utilisez Swagger pour documenter votre API.
- Tests unitaires : Ajoutez des tests unitaires avec Jest pour s'assurer que chaque partie de votre application fonctionne correctement.
Défi pratique
Créez une API REST pour un gestionnaire de notes (notes, tâches à faire). Implémentez les routes pour créer, lire, mettre à jour et supprimer des notes. Assurez-vous que votre API est sécurisée avec JWT et qu'elle est bien documentée.