1. Concepts fondamentaux du routage
Méthodes HTTP
Le routage dans ExpressJS fait référence à la façon dont une application répond à une requête client pour un endpoint spécifique, qui est une URI (ou un chemin) et une méthode HTTP spécifique.
Les principales méthodes HTTP prises en charge par Express sont :
- GET : Récupérer des données
- POST : Créer de nouvelles données
- PUT : Mettre à jour des données existantes (remplacement complet)
- PATCH : Mettre à jour partiellement des données existantes
- DELETE : Supprimer des données
- OPTIONS : Obtenir les options de communication pour une ressource
- HEAD : Similaire à GET mais ne retourne que les en-têtes
La syntaxe de base pour définir une route dans Express est :
1app.METHOD(PATH, HANDLER);
Où :
app
est une instance d'expressMETHOD
est une méthode HTTP en minusculesPATH
est le chemin sur le serveurHANDLER
est la fonction exécutée lorsque la route est mise en correspondance
Création de routes simples
Voici des exemples de routes simples pour différentes méthodes HTTP :
1const express = require('express'); 2const app = express(); 3 4// Route GET pour la page d'accueil 5app.get('/', (req, res) => { 6 res.send('Page d\'accueil'); 7}); 8 9// Route POST pour soumettre des données 10app.post('/submit', (req, res) => { 11 res.send('Données reçues'); 12}); 13 14// Route PUT pour mettre à jour une ressource 15app.put('/users/:id', (req, res) => { 16 res.send(`Mise à jour de l'utilisateur ${req.params.id}`); 17}); 18 19// Route DELETE pour supprimer une ressource 20app.delete('/users/:id', (req, res) => { 21 res.send(`Suppression de l'utilisateur ${req.params.id}`); 22}); 23 24app.listen(3000, () => { 25 console.log('Serveur démarré sur le port 3000'); 26});
Paramètres de route
Les paramètres de route sont des segments d'URL nommés qui sont utilisés pour capturer les valeurs spécifiées à leur position dans l'URL. Les valeurs capturées sont stockées dans l'objet req.params
.
1// Route avec un paramètre 2app.get('/users/:userId', (req, res) => { 3 res.send(`Profil de l'utilisateur: ${req.params.userId}`); 4}); 5 6// Route avec plusieurs paramètres 7app.get('/users/:userId/posts/:postId', (req, res) => { 8 const { userId, postId } = req.params; 9 res.send(`Post ${postId} de l'utilisateur ${userId}`); 10});
Les paramètres de route peuvent être utilisés pour créer des API RESTful, où l'identifiant de la ressource est inclus dans l'URL.
Query strings
Les query strings (chaînes de requête) sont une partie de l'URL qui suit un point d'interrogation (?
) et contient des paires clé-valeur séparées par des esperluettes (&
). Express analyse automatiquement ces chaînes et les rend disponibles via l'objet req.query
.
1// Route avec query string 2// Exemple: /search?q=express&limit=10 3app.get('/search', (req, res) => { 4 const { q, limit } = req.query; 5 res.send(`Recherche de: ${q}, limite: ${limit || 'non spécifiée'}`); 6});
Les query strings sont particulièrement utiles pour :
- Filtrer des résultats
- Paginer des données
- Trier des résultats
- Spécifier des options de recherche
2. Routes avancées
Routes avec expressions régulières
Express permet d'utiliser des expressions régulières dans les chemins de route pour une correspondance plus flexible :
1// Route qui correspond à tout chemin contenant "api" 2app.get(/.*api.*/, (req, res) => { 3 res.send('Cette route contient "api" dans son chemin'); 4}); 5 6// Route qui correspond aux fichiers avec une extension .json 7app.get(/^\/data\/.*\.json$/, (req, res) => { 8 res.send('Vous avez demandé un fichier JSON dans le répertoire data'); 9});
Attention : Dans Express 5, la gestion des caractères spéciaux comme ?
, +
, *
, et ()
a changé par rapport à Express 4.
Routes paramétrées
Les routes paramétrées peuvent être rendues plus flexibles avec des modèles de correspondance :
1// Paramètre optionnel avec ? 2// Correspond à /user et /user/42 3app.get('/user/:id?', (req, res) => { 4 if (req.params.id) { 5 res.send(`Utilisateur avec ID: ${req.params.id}`); 6 } else { 7 res.send('Liste des utilisateurs'); 8 } 9}); 10 11// Paramètre avec contrainte de format (Express 4.x) 12app.get('/user/:id(\\d+)', (req, res) => { 13 // Ne correspond qu'aux IDs numériques 14 res.send(`Utilisateur avec ID numérique: ${req.params.id}`); 15});
Gestion des routes avec des callbacks multiples
Express permet d'utiliser plusieurs fonctions de callback pour une même route, ce qui est utile pour diviser la logique en étapes :
1// Middleware de vérification d'authentification 2function checkAuth(req, res, next) { 3 if (req.query.token === 'secret') { 4 next(); // Passe au middleware suivant 5 } else { 6 res.status(401).send('Non autorisé'); 7 } 8} 9 10// Middleware de vérification des permissions 11function checkPermission(req, res, next) { 12 if (req.params.id === '1' || req.params.id === '2') { 13 next(); 14 } else { 15 res.status(403).send('Accès interdit'); 16 } 17} 18 19// Route avec plusieurs middlewares 20app.get('/admin/:id', checkAuth, checkPermission, (req, res) => { 21 res.send(`Bienvenue admin ${req.params.id}`); 22});
Chaînage de routes
Express permet de chaîner les définitions de routes pour le même chemin mais avec différentes méthodes HTTP :
1app.route('/book') 2 .get((req, res) => { 3 res.send('Obtenir tous les livres'); 4 }) 5 .post((req, res) => { 6 res.send('Ajouter un nouveau livre'); 7 }) 8 .put((req, res) => { 9 res.send('Mettre à jour tous les livres'); 10 }); 11 12app.route('/book/:id') 13 .get((req, res) => { 14 res.send(`Obtenir le livre ${req.params.id}`); 15 }) 16 .put((req, res) => { 17 res.send(`Mettre à jour le livre ${req.params.id}`); 18 }) 19 .delete((req, res) => { 20 res.send(`Supprimer le livre ${req.params.id}`); 21 });
Cette approche réduit la duplication de code et améliore la lisibilité.
3. Organisation des routes
Création de routeurs modulaires
Pour les applications plus grandes, il est recommandé d'organiser les routes en modules séparés à l'aide de express.Router()
. Cela permet de :
- Créer des routeurs modulaires et montables
- Encapsuler les routes liées à une ressource spécifique
- Améliorer la maintenabilité du code
Exemple d'organisation :
1// routes/users.js 2const express = require('express'); 3const router = express.Router(); 4 5// Middleware spécifique à ce routeur 6router.use((req, res, next) => { 7 console.log('Time:', Date.now()); 8 next(); 9}); 10 11// Routes pour les utilisateurs 12router.get('/', (req, res) => { 13 res.send('Liste des utilisateurs'); 14}); 15 16router.get('/:id', (req, res) => { 17 res.send(`Détails de l'utilisateur ${req.params.id}`); 18}); 19 20router.post('/', (req, res) => { 21 res.send('Création d\'un nouvel utilisateur'); 22}); 23 24module.exports = router;
1// routes/products.js 2const express = require('express'); 3const router = express.Router(); 4 5router.get('/', (req, res) => { 6 res.send('Liste des produits'); 7}); 8 9router.get('/:id', (req, res) => { 10 res.send(`Détails du produit ${req.params.id}`); 11}); 12 13module.exports = router;
1// app.js 2const express = require('express'); 3const app = express(); 4 5// Importation des routeurs 6const usersRouter = require('./routes/users'); 7const productsRouter = require('./routes/products'); 8 9// Montage des routeurs sur des chemins spécifiques 10app.use('/users', usersRouter); 11app.use('/products', productsRouter); 12 13app.listen(3000, () => { 14 console.log('Serveur démarré sur le port 3000'); 15});
Utilisation de express.Router()
Le routeur Express est un objet de routage complet qui fonctionne comme une mini-application capable de réaliser des opérations de middleware et de routage.
Caractéristiques importantes :
- Chaque routeur est une instance de middleware et de routage
- Les routeurs peuvent être imbriqués
- Les routeurs peuvent avoir leurs propres middlewares
- Les paramètres de route sont préservés lors du montage
1// Exemple de routeur avec paramètre 2const router = express.Router(); 3 4// Middleware pour ce routeur uniquement 5router.use('/:id', (req, res, next) => { 6 console.log(`ID demandé: ${req.params.id}`); 7 next(); 8}); 9 10// Gestion des paramètres 11router.param('id', (req, res, next, id) => { 12 // Validation ou traitement du paramètre 13 if (isNaN(id)) { 14 return res.status(400).send('ID invalide'); 15 } 16 req.userId = parseInt(id, 10); 17 next(); 18}); 19 20router.get('/:id', (req, res) => { 21 res.send(`Utilisateur avec ID: ${req.userId}`); 22}); 23 24// Montage du routeur 25app.use('/users', router); 26// Accessible via /users/123
Bonnes pratiques d'organisation
Pour maintenir une application Express organisée et évolutive, suivez ces bonnes pratiques :
-
Structure par domaine : Organisez les routes par domaine fonctionnel (utilisateurs, produits, etc.)
-
Séparation des préoccupations : Séparez les routes, les contrôleurs et les modèles
routes/
├── index.js # Exporte tous les routeurs
├── users.js # Routes liées aux utilisateurs
└── products.js # Routes liées aux produits
controllers/
├── userController.js
└── productController.js
models/
├── User.js
└── Product.js
- Contrôleurs dédiés : Déplacez la logique des gestionnaires de route dans des contrôleurs dédiés
1// controllers/userController.js 2exports.getAllUsers = (req, res) => { 3 // Logique pour récupérer tous les utilisateurs 4 res.send('Liste des utilisateurs'); 5}; 6 7exports.getUserById = (req, res) => { 8 // Logique pour récupérer un utilisateur par ID 9 res.send(`Utilisateur ${req.params.id}`); 10}; 11 12// routes/users.js 13const express = require('express'); 14const router = express.Router(); 15const userController = require('../controllers/userController'); 16 17router.get('/', userController.getAllUsers); 18router.get('/:id', userController.getUserById); 19 20module.exports = router;
-
Middlewares spécifiques aux routes : Utilisez des middlewares spécifiques pour chaque groupe de routes
-
Gestion centralisée des erreurs : Utilisez un middleware d'erreur global
Versionnement d'API
Pour les API destinées à évoluer dans le temps, le versionnement est essentiel :
- Versionnement par URL :
1// v1 API 2const v1Router = express.Router(); 3app.use('/api/v1', v1Router); 4 5v1Router.get('/users', (req, res) => { 6 // Implémentation v1 7}); 8 9// v2 API 10const v2Router = express.Router(); 11app.use('/api/v2', v2Router); 12 13v2Router.get('/users', (req, res) => { 14 // Implémentation v2 améliorée 15});
- Versionnement par en-tête :
1app.get('/api/users', (req, res) => { 2 const apiVersion = req.headers['accept-version'] || '1.0'; 3 4 if (apiVersion === '1.0') { 5 // Implémentation v1 6 } else if (apiVersion === '2.0') { 7 // Implémentation v2 8 } else { 9 res.status(400).send('Version API non supportée'); 10 } 11});
- Versionnement par paramètre de requête :
1app.get('/api/users', (req, res) => { 2 const apiVersion = req.query.version || '1'; 3 4 if (apiVersion === '1') { 5 // Implémentation v1 6 } else if (apiVersion === '2') { 7 // Implémentation v2 8 } else { 9 res.status(400).send('Version API non supportée'); 10 } 11});
Le versionnement par URL est généralement préféré pour sa simplicité et sa visibilité.
Exercices pratiques
Exercice 1: Créer un ensemble de routes pour une API de gestion de tâches
Créez une API RESTful pour gérer une liste de tâches avec les routes suivantes :
- GET /tasks - Récupérer toutes les tâches
- GET /tasks/:id - Récupérer une tâche spécifique
- POST /tasks - Créer une nouvelle tâche
- PUT /tasks/:id - Mettre à jour une tâche existante
- DELETE /tasks/:id - Supprimer une tâche
Exercice 2: Implémenter des routes paramétrées
Créez une API pour un blog avec des routes paramétrées :
- GET /posts/:year/:month? - Récupérer les articles d'une année et optionnellement d'un mois spécifique
- GET /categories/:categoryName/posts - Récupérer les articles d'une catégorie spécifique
Exercice 3: Organiser une application avec des routeurs modulaires
Réorganisez une application Express existante en utilisant des routeurs modulaires pour différentes ressources (utilisateurs, produits, commandes, etc.).