Kodokon kodokon.com

Validation des entrées et middleware d'erreur

Validez les données au bord de l'API et centralisez toute la gestion d'erreurs dans un middleware unique à quatre paramètres.

9 min · 3 questions

Ouvrir cette leçon dans Kodokon

Toute donnée entrante est hostile jusqu'à preuve du contraire : corps JSON, paramètres d'URL, en-têtes. La validation appartient au bord de l'application - avant le contrôleur - pour que les couches internes travaillent sur des données garanties. Distinguez ensuite deux familles d'erreurs : les erreurs opérationnelles (entrée invalide, ressource absente, conflit), attendues et traduites en 4xx, et les erreurs de programmation (bugs), qui doivent produire un 500 opaque côté client et une trace complète côté serveur.

JAVASCRIPT
export class HttpError extends Error {
  constructor(status, message, details = undefined) {
    super(message);
    this.status = status;
    this.details = details;
  }
}
Une erreur qui transporte son statut HTTP : la brique de base.

Un middleware de validation intercepte la requête avant le contrôleur. La version manuelle ci-dessous montre le principe ; en production, un schéma déclaratif (zod s'est imposé comme standard) apporte en plus l'inférence de types et des messages structurés. Le compromis : une dépendance et un léger coût à l'exécution, contre des règles centralisées, composables et impossibles à oublier.

JAVASCRIPT
import { HttpError } from './http-error.js';

export function validateUser(req, res, next) {
  const { email, name } = req.body ?? {};
  const errors = [];
  if (typeof email !== 'string' || !email.includes('@')) {
    errors.push('email: invalid format');
  }
  if (typeof name !== 'string' || name.trim().length < 2) {
    errors.push('name: 2 characters minimum');
  }
  if (errors.length > 0) {
    return next(new HttpError(400, 'Invalid body', errors));
  }
  next();
}
Brancher avec router.post('/', validateUser, controller.create).

Express reconnaît un middleware d'erreur à sa signature à quatre paramètres (err, req, res, next). Enregistré en dernier, après toutes les routes avec app.use(errorHandler), il devient l'unique sortie d'erreur de l'API : format de réponse homogène, journalisation en un seul point, aucune fuite d'information interne. Les contrôleurs, eux, se contentent d'appeler next(err).

JAVASCRIPT
export function errorHandler(err, req, res, next) {
  if (res.headersSent) {
    return next(err);
  }
  const status = err.status ?? 500;
  const body = { error: err.message };
  if (err.details) {
    body.details = err.details;
  }
  if (status >= 500) {
    console.error(err);
    body.error = 'Internal server error';
  }
  res.status(status).json(body);
}
4xx transparents pour le client, 500 masqués mais tracés en log.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Comment Express distingue-t-il un middleware d'erreur d'un middleware ordinaire ?
    • Par son nom, qui doit contenir le mot error
    • Par sa signature à quatre paramètres (err, req, res, next)
    • Par sa position en premier dans la chaîne
    • Par un appel explicite à app.setErrorHandler
  2. Quelle est la bonne réponse HTTP face à une erreur de programmation inattendue ?
    • Un 500 avec le message et la pile pour aider au diagnostic
    • Un 400 pour signaler que la requête a échoué
    • Un 500 au message générique, l'erreur complète étant journalisée côté serveur
  3. Dans Express 4, que se passe-t-il si un handler async rejette sans try/catch ?
    • L'erreur atteint quand même le middleware d'erreur
    • La requête reste suspendue : le rejet n'est jamais transmis à next
    • Express renvoie automatiquement un 400
    • Le processus Node s'arrête immédiatement