4.1Construire une API REST complète (CRUD)
L'essentiel en 30 secondes
- REST = ressources + méthodes HTTP : l'URL désigne la ressource au pluriel (
/users), la méthode porte l'action (GET lire, POST créer, PUT modifier, DELETE supprimer). - Structurer en couches (MVC) : routes (URL) → contrôleurs (logique) → modèles (données) ;
express.Router()regroupe les routes par ressource. - Réflexe de Salim : renvoyer du JSON avec le bon code (201 après création, 404 si absent, 400 si invalide) et versionner (
/api/v1/).
Mini-cas filé — Salim construit l'API produits à Alger
Salim, développeur junior dans une startup à Alger, doit exposer le catalogue produits au front : lister, afficher un produit, en créer, modifier, supprimer. Il conçoit d'abord ses endpoints REST (GET /api/v1/produits, GET /api/v1/produits/:id, POST, PUT, DELETE), sépare routes, contrôleurs et données, et regroupe les routes dans un express.Router() monté sur l'app. Quand le front appelle son API, chaque réponse arrive en JSON avec le bon code de statut. Premier jalon du fil rouge : une API prévisible que le front devine sans documentation.
Ce même cas revient dans le cours, le schéma, le script et le défi 48h — et continue en 4.2, 4.3 et 4.4.
1. Les principes REST
REST est un style d'architecture d'API : on expose des ressources (utilisateurs, produits) identifiées par des URL claires au pluriel (/users, /produits), et on agit dessus avec les méthodes HTTP selon leur sémantique : GET /users (lister), GET /users/:id (lire un), POST /users (créer), PUT/PATCH /users/:id (modifier), DELETE /users/:id (supprimer). Comme Salim, on ne met jamais de verbe dans l'URL : pas /getUsers, mais GET /users.
L'API renvoie des données en JSON avec les bons codes de statut : 200 succès, 201 créé (après POST), 204 sans contenu (après DELETE), 400 requête invalide, 404 non trouvé, 500 erreur serveur. Ces conventions cohérentes rendent l'API prévisible : le front devine les endpoints au lieu de les mémoriser un par un.
2. Structurer en couches (MVC)
Une API qui grossit sans structure devient un plat de spaghetti. Le pattern classique est le MVC : les modèles (structure et accès aux données), les contrôleurs (logique de traitement de chaque requête), les routes (définition des URL et méthodes, qui délèguent aux contrôleurs). Dossiers typiques : routes/, controllers/, models/, plus middlewares/, services/, config/.
On utilise le Router d'Express (express.Router()) pour regrouper les routes par ressource dans des fichiers séparés (un routeur produits, un routeur commandes), montés sur l'app. Cette séparation rend le code compréhensible, maintenable, testable et évolutif — et permet de travailler à plusieurs sans se marcher dessus.
3. Les bonnes pratiques d'une API bien conçue
Ce qui sépare une API professionnelle d'une API qui « marche » : des URL cohérentes au pluriel, les bonnes méthodes pour les bonnes actions, les bons codes de statut (jamais 200 pour une erreur), une gestion d'erreurs propre (voir 4.4), la pagination des listes, le versionnage (/api/v1/...) pour faire évoluer l'API sans casser les clients, et des réponses JSON structurées. Salim applique cette check-list à chaque endpoint avant de dire « c'est fini ».
🔎 Exemple concret — 🔎 Exemple concret — Le CRUD REST d'une ressource
La table de correspondance que Salim affiche au-dessus de son écran :
GET /api/v1/produits → lister (200)
GET /api/v1/produits/:id → lire un (200, 404 si absent)
POST /api/v1/produits → créer (201, 400 si invalide)
PUT /api/v1/produits/:id → modifier (200, 404 si absent)
DELETE /api/v1/produits/:id → supprimer (204)
Lecture : cinq lignes, cinq couples méthode + URL, cinq codes attendus.
Leçon : l'action est portée par la méthode HTTP, jamais par l'URL.
Vocabulaire de la section
- API REST
- Style d'architecture d'API web : exposer des ressources (URL claires) manipulées par les méthodes HTTP selon leur sémantique, réponses en JSON avec les bons codes de statut.
Exemple simple : L'API produits de Salim : GET pour lire, POST pour créer, JSON en réponse.
Ce que ce n'est pas : un simple script qui renvoie du HTML mélangé à la logique.
Réflexe : pour chaque endpoint, nommer ressource, méthode et code attendu.
- Ressource
- Entité exposée par l'API (produits, utilisateurs, commandes), identifiée par une URL claire au pluriel.
Exemple simple : La ressource produits de Salim vit sous /api/v1/produits.
Ce que ce n'est pas : une action verbale comme /creerProduit.
Réflexe : URL = nom au pluriel, sans verbe.
- MVC
- Séparation modèles (données), contrôleurs (logique), routes (URL) pour un code maintenable et évolutif.
Exemple simple : Salim place ses URL dans routes/, sa logique dans controllers/.
Ce que ce n'est pas : tout le code entassé dans un seul fichier app.js.
Réflexe : une modification touche une couche, pas les trois.
- express.Router()
- Outil Express regroupant les routes d'une ressource dans un fichier séparé, monté ensuite sur l'application.
Exemple simple : Le routeur produits de Salim est monté avec app.use('/api/v1/produits', routeur).
Ce que ce n'est pas : la déclaration de toutes les routes en vrac dans le fichier principal.
Réflexe : un fichier de routes par ressource.
- Code de statut
- Nombre HTTP communiquant le résultat : 200 succès, 201 créé, 204 sans contenu, 400 invalide, 404 absent, 500 erreur serveur.
Exemple simple : Salim renvoie 201 après un POST réussi, 404 pour un produit inconnu.
Ce que ce n'est pas : un 200 systématique même en cas d'erreur.
Réflexe : le client doit comprendre le résultat sans lire le corps.
En pratique — En pratique — Construire une API REST structurée
- Concevez les endpoints d'une ressource selon REST : GET /items (lister), GET /items/:id (lire), POST /items (créer), PUT /items/:id (modifier), DELETE /items/:id (supprimer).
- Structurez le projet en couches : routes/ (URL), controllers/ (logique), models/ (données) — comme Salim pour son catalogue.
- Utilisez express.Router() pour regrouper les routes d'une ressource dans un fichier, monté sur l'app (app.use('/api/v1/items', itemsRouter)).
- Renvoyez du JSON avec les bons codes de statut (201 après création, 404 si non trouvé, 400 si données invalides) et prévoyez pagination et versionnage.
Script prêt à dire — présenter son API au front (30 s)
« C'est Salim : l'API produits suit les conventions REST — GET pour lire, POST pour créer, PUT pour modifier, DELETE pour supprimer, sous /api/v1/produits, réponses en JSON avec les bons codes : 201 après création, 404 si absent, 400 si invalide. La structure est en couches routes, contrôleurs, modèles, donc on pourra ajouter des ressources sans tout casser. »
À dire en revue de code ; montrez la table des cinq endpoints.
Modèle copier-coller — routeur CRUD Express (code testé)
const express = require('express');
const routeur = express.Router();
let produits = [];
let prochainId = 1;
// GET /api/v1/produits → lister (200)
routeur.get('/', (req, res) => {
res.json(produits);
});
// GET /api/v1/produits/:id → lire un (200 / 404)
routeur.get('/:id', (req, res) => {
const produit = produits.find((p) => p.id === Number(req.params.id));
if (!produit) return res.status(404).json({ erreur: 'Produit introuvable' });
res.json(produit);
});
// POST /api/v1/produits → créer (201 / 400)
routeur.post('/', (req, res) => {
if (!req.body.nom) return res.status(400).json({ erreur: 'Le champ nom est requis' });
const produit = { id: prochainId++, nom: req.body.nom };
produits.push(produit);
res.status(201).json(produit);
});
// PUT /api/v1/produits/:id → modifier (200 / 404)
routeur.put('/:id', (req, res) => {
const produit = produits.find((p) => p.id === Number(req.params.id));
if (!produit) return res.status(404).json({ erreur: 'Produit introuvable' });
if (req.body.nom) produit.nom = req.body.nom;
res.json(produit);
});
// DELETE /api/v1/produits/:id → supprimer (204 / 404)
routeur.delete('/:id', (req, res) => {
const index = produits.findIndex((p) => p.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ erreur: 'Produit introuvable' });
produits.splice(index, 1);
res.status(204).end();
});
module.exports = routeur;
// Montage (nécessite express.json()) :
// const express = require('express');
// const app = express();
// app.use(express.json());
// app.use('/api/v1/produits', routeur);
Prototype jetable
Une séparation légère suffit (routes + logique), mais gardez les conventions REST : elles ne coûtent rien et évitent de réécrire.
API réelle en équipe
Comme Salim : couches complètes (routes/, controllers/, models/, services/), un routeur par ressource, revue de code sur les statuts.
API consommée par un mobile
Soignez pagination et réponses JSON stables : le mobile met en cache et souffre de chaque rupture de contrat.
API qui doit évoluer
Versionnez dès le départ (/api/v1/) : la v2 viendra, et vos clients v1 ne casseront pas.
✓ Bonnes pratiques
- URL = ressource au pluriel, action = méthode HTTP.
- Structurer en couches dès que l'API est réelle.
- Renvoyer le bon code de statut à chaque fois.
✗ Pièges fréquents
- Mettre un verbe dans l'URL (/getProduits).
- Renvoyer 200 pour tout, même les erreurs.
- Entasser toute la logique dans les fichiers de routes.
Points clés à retenir
- API REST : exposer des RESSOURCES (URL claires au pluriel) manipulées par les MÉTHODES HTTP selon leur sémantique, en JSON avec les bons CODES DE STATUT.
- CRUD REST : GET /produits (lister), GET /produits/:id (lire), POST (créer, 201), PUT (modifier), DELETE (supprimer, 204).
- STRUCTURER en couches (MVC) : models, controllers, routes + services/middlewares ; express.Router() regroupe les routes par ressource.
- Bonnes pratiques : URL cohérentes, bons codes, gestion d'erreurs, pagination, versionnage (/api/v1/). Prévisible et professionnelle.
Défi 48h — appliquez demain
Construisez le routeur CRUD complet d'1 ressource réelle (les 5 endpoints, bons codes) et testez chaque route y compris le 404 et le 400.
- Je peux citer les 5 endpoints CRUD avec leurs codes.
- Je sais séparer routes, contrôleurs et modèles.
- J'ai testé 1 routeur complet, échecs inclus.
Questions fréquentes
Qu'est-ce qui distingue une API REST « bien conçue » d'une API qui « marche » simplement ?
Des conventions et une structure : URL cohérentes sur les ressources (pas de verbe), bonnes méthodes HTTP selon leur sémantique, bons codes de statut (201, 404, 400 — pas 200 pour tout), couches MVC au lieu du spaghetti, gestion d'erreurs propre (4.4), validation des entrées, réponses JSON structurées, pagination et versionnage (/api/v1/). Une API bien conçue est prévisible (on devine ses endpoints), maintenable et agréable à consommer — cruciale car elle est faite pour être utilisée par d'autres.
Faut-il vraiment structurer en MVC dès le début, même pour une petite API ?
Adaptez au projet, mais séparez tôt : les projets grandissent presque toujours et refactoriser un spaghetti coûte cher. Petit prototype : séparation légère (routes vs logique, avec express.Router()). API réelle : couches complètes dès le début (routes/, controllers/, models/, services/) — compréhensible, maintenable, testable et collaborative. Ni sur-ingénierie pour un exercice, ni fourre-tout « parce que c'est petit ».
Dans une API REST, qui porte l'action (créer, supprimer) ?