Aller au contenu

Erreurs & internationalisation

Le flux d'erreur traverse trois éléments : les classes d'erreur de domaine (domain/errors.ts), les filtres d'exception HTTP (web/exception.http-filter.ts) qui les traduisent en réponses, et le service i18n (app/i18n/) qui localise les messages.

Règle d'or

Toujours lever une erreur de domaine

Les services lèvent des classes d'erreur de domaine — jamais throw new Error('...'), qui court-circuite le filtre HTTP et produit un 500.

const record = await Workspaces.findByPk(id);
if (!record) throw new NotFoundError();

Classes d'erreur de domaine

Classe Statut HTTP Charge utile
ValidationError 400 { [champ]: string[] }
AuthenticationError 401 { message, code, workspaces? }
AuthorizationError 403 { message }
NotFoundError 404 { message }
ConflictError 409 { message, code? }
RateLimitError 429 { message } + en-tête Retry-After
NotImplementedError 501 { message }
PaymentGatewayError 503 { message }
LedgerError 500 format NestJS par défaut (voir ci-dessous)

Les erreurs Zod (ZodError) sont également mappées en 400, sous le même format dictionnaire que ValidationError.

LedgerError est volontairement non filtrée

LedgerError protège un invariant de comptabilité en partie double construit côté service, pas une entrée client. Elle n'a aucun filtre @Catch : elle résout en 500 et reste visible pour le suivi d'erreurs (Sentry). Ne la repliez jamais dans ValidationError (400), ce qui ferait passer un bug comptable pour une erreur utilisateur et éteindrait l'alerte.

Le code machine de AuthenticationError

AuthenticationError porte un code machine-lisible que le frontend utilise pour brancher sa logique (rafraîchir le token, proposer un sélecteur de workspace, écran d'activation…) :

invalid_credentials | missing_authorization | workspace_header_required
| no_membership | account_not_activated | user_suspended

Pour workspace_header_required, l'erreur transporte aussi workspaceOptions → la liste workspaces renvoyée au client. Détail complet et tableau des actions frontend : Erreurs API.

Localisation (i18n)

Tous les messages sont localisés par le backend : le champ message (et les messages de validation) contient du texte prêt à afficher, traduit selon :

  1. la langue du compte (user.language) si la requête est authentifiée ;
  2. sinon l'en-tête Accept-Language ;
  3. sinon la langue par défaut.

Les classes d'erreur transportent une clé de message (messageKey, ex. errors.common.not_found) et d'éventuels params, pas du texte figé. Le filtre HTTP résout la clé en texte localisé au moment de la réponse.

Élément Emplacement
Service i18n app/i18n/i18n.service.ts
Locales (FR/EN) app/i18n/locales/
Mapping d'erreurs Zod app/i18n/zod-error-map.ts (clés i18n pour la validation)
Résolution de locale app/i18n/locale.ts

Côté frontend

Le frontend ne re-traduit pas ces messages. Pour brancher un comportement, il s'appuie sur le status code et, quand il existe, le champ code — jamais sur le texte du message.

Filtres et interception globale

Les filtres d'exception sont enregistrés dans web/app.module.ts (APP_FILTER), aux côtés du filtre global Sentry et de l'intercepteur de contexte. Voir Erreurs API pour le contrat HTTP complet exposé au frontend.