Leçon 9 / 15

Centraliser les erreurs asynchrones

15 min Node.js — API Express et code asynchrone Gratuit

Objectif : Transmettre les rejets des contrôleurs asynchrones à un gestionnaire d’erreurs sans dupliquer les try/catch.

Un service peut échouer alors qu’un contrôleur attend son résultat. Si ce rejet n’est pas transmis à Express, la requête peut rester sans réponse et l’erreur ne suit pas le format JSON de l’API. Vous allez relier les contrôleurs asynchrones à un gestionnaire unique, sans ajouter de try/catch dans chaque contrôleur.

Transmettre les rejets des contrôleurs

Une fonction déclarée async retourne une promesse : vous avez utilisé await dans le contrôleur des commandes à la leçon précédente. En cas d’échec, cette promesse est rejetée. Express 5 transmet automatiquement les rejets des gestionnaires de route à son mécanisme d’erreurs, mais Express 4 ne le fait pas pour une promesse retournée.

Pour que le projet fonctionne également avec Express 4, adaptez le contrôleur au moment de l’enregistrer dans le routeur. Le petit adaptateur ci-dessous transforme tout rejet en appel à next, qui transmet l’erreur à la chaîne de middlewares d’Express. Il fonctionne aussi avec Express 5 et évite d’éparpiller une logique de transmission dans les contrôleurs.

routes/commandes.jsJavaScript
const express = require("express");
const commandesControleur = require("../controleurs/commandes");

const routeur = express.Router();

const transmettreErreurAsync = (controleur) => (req, res, next) => {
  Promise.resolve(controleur(req, res, next)).catch(next);
};

routeur.get("/", transmettreErreurAsync(commandesControleur.lister));

module.exports = routeur;
Aperçu du rendu
Chargement de l'aperçu…

L’adaptateur enveloppe l’appel du contrôleur dans une promesse, puis lui associe .catch(next). Si le contrôleur réussit, Express reçoit la réponse construite par ce contrôleur ; s’il échoue, l’erreur est transmise à la suite. Vous pouvez appliquer le même principe à une autre route asynchrone, sans ajouter un fichier qui ne figure pas dans l’arborescence du projet.

Construire le gestionnaire central

Un middleware de gestion d’erreurs Express se distingue par ses quatre paramètres : err, req, res et next. Créez-le dans le fichier prévu et faites suivre chaque réponse de la même enveloppe JSON que les réponses normales. Pour une erreur inattendue, ne renvoyez pas le message interne de l’exception au client.

middlewares/erreurs.jsJavaScript
function gererErreurs(err, req, res, next) {
  const statut = err.status || err.statusCode || 500;
  const erreurInterne = statut >= 500;

  res.status(statut).json({
    donnees: null,
    erreur: {
      code: erreurInterne ? "ERREUR_INTERNE" : (err.code || "REQUETE_INVALIDE"),
      message: erreurInterne ? "Une erreur interne est survenue" : err.message
    }
  });
}

module.exports = gererErreurs;
Aperçu du rendu
Chargement de l'aperçu…

Le statut provient de err.status ou de err.statusCode ; en l’absence de l’un ou l’autre, le gestionnaire choisit 500. Pour les erreurs serveur, il utilise un code et un message génériques. Les erreurs contrôlées peuvent fournir leur propre statut, code et message ; les validations de la création de commande seront ajoutées dans une prochaine leçon.

Monter le middleware en dernier

Express parcourt les middlewares dans l’ordre où vous les enregistrez. Montez donc le gestionnaire après les routes : si un contrôleur transmet une erreur avec next(err), Express peut alors atteindre ce gestionnaire. Voici le serveur complet à cet état du projet, avec ses routeurs de ressources et le middleware terminal.

serveur.jsJavaScript
const express = require("express");
const commandesRouteur = require("./routes/commandes");
const produitsRouteur = require("./routes/produits");
const gererErreurs = require("./middlewares/erreurs");

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

app.use(express.json());
app.use("/api/commandes", commandesRouteur);
app.use("/api/produits", produitsRouteur);
app.use(gererErreurs);

app.listen(port, () => {
  console.log(`API disponible sur le port ${port}`);
});
Aperçu du rendu
Chargement de l'aperçu…

Vérifier le parcours d’une erreur

Le contrôleur reste une fonction async qui attend commandesService.lister() et prépare la réponse de succès ; ne changez pas sa logique pour cette leçon. Si le service rejette sa promesse, l’adaptateur transmet l’erreur au gestionnaire central. Le client reçoit alors le statut 500 et un corps d’erreur normalisé, plutôt qu’une réponse contenant les détails de l’exception.

À ce stade, le service des commandes retourne toujours une promesse résolue avec une liste vide ; le parcours d’échec s’activera dès qu’une opération asynchrone rejettera sa promesse. Vous n’avez pas besoin de provoquer artificiellement une erreur en modifiant le service. Les erreurs de requête et les réponses négatives seront exercées avec les scénarios HTTP prévus plus loin dans le cours.

À retenir

  • Un rejet asynchrone doit être transmis à Express pour ne pas laisser la requête sans réponse.
  • L’adaptateur avec Promise.resolve(...).catch(next) rend cette transmission explicite et prend en charge Express 4.
  • Le middleware d’erreurs a quatre paramètres et doit être monté après les routeurs.
  • Le gestionnaire renvoie une enveloppe JSON normalisée et masque les détails des erreurs internes.

La prochaine leçon ajoutera un middleware de validation au parcours de création d’une commande ; ses erreurs pourront emprunter ce même flux central.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.