Dans une API backend, un middleware est la couche qui s’intercale entre l’arrivée d’une requête et l’exécution finale d’une route ou d’un contrôleur. Il sert à vérifier, enrichir, filtrer ou bloquer le flux selon des règles transverses comme l’authentification, la validation ou la sécurité. Bien compris, ce mécanisme rend une application plus lisible, plus sûre et plus simple à faire évoluer.
Voici l’essentiel à retenir avant de l’implémenter dans une API
- Un middleware est une fonction ou une couche intermédiaire qui traite une requête avant, pendant ou après la suite du pipeline.
- Il est idéal pour l’authentification, la validation, le logging, les règles CORS et la limitation de débit.
- Il peut modifier la requête, enrichir la réponse, arrêter le flux ou transmettre le contrôle selon le framework.
- Il ne doit pas contenir la logique métier principale, qui appartient plutôt au contrôleur ou au service.
- L’ordre d’exécution compte autant que le code lui-même : un mauvais enchaînement produit des bugs subtils.

Ce qu’un middleware fait vraiment dans le pipeline
Je résume souvent le middleware comme un poste de contrôle placé sur le chemin d’une requête HTTP. Il reçoit le flux entrant, peut lire ce qui a été envoyé, ajouter des informations utiles, décider de laisser passer la requête ou de l’arrêter immédiatement. Dans Express, la documentation officielle le formule très simplement : une fonction middleware a accès à req, res et next.
- Lire la requête pour récupérer des en-têtes, un jeton, un identifiant de route ou un corps de formulaire.
- Modifier certaines données avant qu’elles n’atteignent le contrôleur, par exemple en ajoutant un identifiant de traçage.
- Bloquer la suite si une condition n’est pas respectée, comme un token invalide ou une origine interdite.
- Déléguer au reste de la chaîne avec la fonction de passage, souvent nommée next().
-
Finaliser la réponse sans aller plus loin, par exemple en renvoyant un
401, un403ou un429.
Ce modèle paraît simple, mais il change beaucoup de choses en pratique : on isole les règles transverses au lieu de les répéter dans chaque endpoint, et on garde des contrôleurs plus lisibles. Une fois cette mécanique comprise, la vraie question devient : dans quels cas faut-il l’utiliser sans en abuser ?
Les usages qui reviennent le plus dans une API
Dans une API, le middleware n’est pas là pour “faire joli”. Il sert surtout à centraliser les traitements qui reviennent partout, ou presque, et qui pollueraient rapidement la logique métier s’ils étaient recopiés route par route.
| Besoin | Ce que fait le middleware | Ce que cela évite |
|---|---|---|
| Authentification | Vérifie un jeton, une session ou une clé API avant d’aller plus loin | Des endpoints protégés “à moitié” et des règles d’accès dispersées |
| Autorisation | Contrôle les rôles, permissions ou scopes | Des accès interdits qui tombent trop tard dans le code |
| Validation | Contrôle le format, les types et les champs d’une requête | Des erreurs métier déclenchées par des données déjà incohérentes |
| Journalisation | Enregistre la route, le statut, la durée, l’utilisateur ou un identifiant de corrélation | Des requêtes impossibles à tracer quand un incident survient |
| CORS | Définit quelles origines peuvent lire la réponse | Des règles de partage d’origine recopiées dans chaque handler |
| Limitation de débit | Compte et freine les requêtes trop fréquentes | Une API exposée sans garde-fou face au spam ou aux abus |
| Normalisation d’erreur | Formate les réponses d’échec de manière cohérente | Un mélange de réponses JSON, HTML et messages bruts difficile à consommer |
Le gain n’est pas seulement organisationnel. Un bon middleware réduit aussi les écarts de comportement entre endpoints : même format d’erreur, mêmes règles d’accès, mêmes en-têtes, même traçabilité. C’est justement ce qui permet ensuite de distinguer les différentes familles de middleware.
Les grands types à connaître selon le niveau où ils agissent
Le mot “middleware” couvre plusieurs réalités. C’est utile, mais cela crée de la confusion si on ne précise pas où il s’exécute dans la chaîne.
Middleware global
Il s’applique à toutes les requêtes ou à presque toutes. Je le réserve aux préoccupations vraiment transverses : en-têtes de sécurité, corrélation d’ID, parsing de base, journalisation minimale. Il est puissant, mais plus il est global, plus il faut surveiller son coût et son ordre d’exécution.
Middleware de route
Il ne s’exécute que sur certaines routes ou certains groupes de routes. C’est souvent le bon choix pour protéger un espace /admin, encadrer un upload ou imposer un contrôle supplémentaire sur un endpoint sensible. On garde ainsi une granularité plus fine sans alourdir tout le reste de l’API.
Middleware d’erreur
Son rôle est de récupérer et formater les erreurs de manière cohérente. Dans Express, on le reconnaît à sa signature particulière, qui inclut l’erreur en premier paramètre. Cette couche est précieuse pour éviter les réponses incohérentes et pour ne pas exposer de détails internes en production.
Middleware tiers et middleware maison
Les middlewares tiers, comme helmet, cors ou express-rate-limit, apportent des comportements courants déjà éprouvés. Les middlewares maison, eux, restent meilleurs quand la règle est spécifique à votre produit : par exemple une vérification métier très ciblée, ou un enrichissement de contexte propre à votre domaine. MDN rappelle d’ailleurs que le terme reste volontairement large, ce qui explique pourquoi chaque framework lui donne une nuance légèrement différente.
En pratique, je préfère toujours la solution la plus simple qui reste lisible : un middleware global pour ce qui est réellement transversal, un middleware de route pour ce qui dépend du contexte, et un middleware d’erreur unique pour normaliser les réponses. C’est cette distinction qui aide ensuite à ne pas confondre middleware, routeur, contrôleur et service.
Middleware, routeur, contrôleur et service ne jouent pas le même rôle
Beaucoup d’API deviennent confuses parce que tout finit dans le mauvais fichier. Le middleware ne remplace pas le contrôleur, et le contrôleur ne doit pas absorber toute la logique métier.
| Composant | Rôle principal | Ce qu’il ne devrait pas faire | Exemple concret |
|---|---|---|---|
| Routeur | Décide quel handler répond à quelle URL ou méthode HTTP | Prendre des décisions métier complexes | Associer GET /users/:id à un contrôleur précis |
| Middleware | Gère les traitements transverses avant, pendant ou après la requête | Contenir la logique métier principale | Vérifier un token, ajouter un trace ID, limiter le débit |
| Contrôleur | Orchestre le cas d’usage de l’endpoint | Porter toutes les règles de domaine | Lire les données, appeler le service, renvoyer la réponse |
| Service | Contient la logique métier et les règles du domaine | Connaître les détails HTTP inutiles | Calculer un prix, vérifier une règle commerciale, écrire en base |
Cette séparation me paraît saine pour une raison simple : chaque couche reste testable et remplaçable sans casser le reste. Quand cette frontière est floue, on passe plus de temps à comprendre le code qu’à livrer une fonctionnalité. La suite logique, c’est donc de voir comment écrire une couche intermédiaire propre.
Écrire un middleware utile sans ralentir l’API
Le piège classique, c’est le middleware fourre-tout. On commence avec une vérification simple, puis on ajoute du mapping, du formatage, de la logique métier et parfois même des appels à la base de données. À ce stade, on a perdu l’intérêt du pattern.
Ce que je recommande
- Une seule responsabilité par middleware : auth, validation, logging, CORS ou gestion d’erreur, pas tout à la fois.
- Un traitement rapide : le middleware doit rester léger, surtout s’il est global.
- Une sortie explicite : soit il appelle la suite, soit il renvoie une réponse, soit il transmet une erreur.
- Un ordre lisible : validation avant traitement, sécurité avant exposition, erreur en dernier.
- Des tests ciblés sur les cas de blocage, les statuts renvoyés et l’ordre d’exécution.
Les erreurs que je vois le plus
- Mettre de la logique métier dans un middleware alors qu’elle devrait vivre dans un service.
- Oublier d’appeler la suite de la chaîne ou d’envoyer une réponse, ce qui bloque la requête.
- Empiler trop de middlewares globaux et créer des effets de bord difficiles à diagnostiquer.
- Changer des objets de requête sans contrat clair, ce qui rend le code fragile pour l’équipe.
- Logguer trop d’informations sensibles sans filtrage, notamment sur les données personnelles ou les tokens.
Si je devais retenir une règle : un middleware doit aider la requête à avancer ou à s’arrêter proprement, pas prendre la place du cœur applicatif. Cette ligne de partage est ce qui permet de garder une API fluide quand le projet grandit.
La bonne question à se poser avant d’en ajouter un de plus
Je me pose toujours la même question avant d’ajouter une nouvelle couche : est-ce que cette logique est transversale, réutilisable et indépendante du métier principal ? Si la réponse est oui, le middleware est souvent le bon endroit. Si la réponse est non, il vaut mieux la laisser dans le contrôleur ou le service concerné.
- Oui si la règle concerne plusieurs routes, plusieurs contrôleurs ou plusieurs domaines de l’API.
- Oui si la règle peut être testée sans dépendre d’un cas métier précis.
- Non si la décision dépend d’un calcul de commande, d’un workflow interne ou d’un état métier complexe.
- Non si la couche risque de devenir un point de passage opaque que personne n’ose modifier.
En résumé, un middleware est moins un outil magique qu’un moyen de garder une API propre : il centralise ce qui se répète, protège ce qui doit l’être et évite de disperser les règles d’infrastructure dans toute la base de code. C’est souvent la différence entre une API qui reste maintenable et une API qui se transforme en empilement de cas particuliers.