# Gestion des erreurs — Projet Batch-cooking > Documentation du contrat d'erreurs partagé entre `apps/api` et `apps/web`. --- ## Vue d'ensemble Trois pièces travaillent ensemble pour que **toute** erreur, du serveur jusqu'à l'affichage utilisateur, passe par un chemin unique et prévisible : - **`packages/shared`** — le contrat : `ErrorCode` (énumération de tous les codes d'erreur métier) et `ApiErrorResponse` (forme JSON de toute réponse d'erreur de l'API). Ni l'API ni le web ne définissent leur propre liste de codes. - **`apps/api` → `ErrorHandlerService`** — centralise la traduction de n'importe quelle erreur levée (validation zod, `HttpError` métier, erreur inattendue) en `{ status, body }` conforme au contrat. Le middleware d'erreur d'Express (`app.ts`) ne fait qu'appeler ce service. - **`apps/web` → `ErrorMessageService`** — centralise la traduction de chaque `ErrorCode` en libellé affichable, avec un système de locale (`fr` aujourd'hui, extensible). Les composants n'écrivent jamais de texte d'erreur en dur. ```mermaid flowchart LR subgraph API["apps/api"] THROW["Route / service
throw new HttpError(status, code, message)"] EHS["ErrorHandlerService.handle()"] THROW --> EHS end EHS -->|"JSON: { code, message, details? }"| HTTP["Réponse HTTP"] subgraph WEB["apps/web"] CLIENT["ApiClient
lève ApiError(status, code, ...)"] EMS["ErrorMessageService.getLabel(code)"] UI["Composant (LoginPage, SignupPage...)"] CLIENT --> EMS --> UI end HTTP --> CLIENT SHARED[("packages/shared
ErrorCode, ApiErrorResponse")] SHARED -. contrat .-> THROW SHARED -. contrat .-> CLIENT SHARED -. contrat .-> EMS style SHARED fill:none,stroke:#888,stroke-width:1px ``` --- ## Le contrat (`packages/shared/src/errors/error-codes.ts`) ```ts enum ErrorCode { VALIDATION_ERROR, EMAIL_ALREADY_IN_USE, INVALID_CREDENTIALS, NOT_AUTHENTICATED, NOT_FOUND, INTERNAL_ERROR, } interface ApiErrorResponse { code: ErrorCode; message: string; // anglais, dev-facing — jamais affiché tel quel côté UI details?: Record; // uniquement pour VALIDATION_ERROR } ``` **Règle** : `message` est destiné aux logs/au débogage (toujours en anglais, jamais localisé). Le texte affiché à l'utilisateur vient **toujours** de `ErrorMessageService.getLabel(code)` côté client, jamais de `message` directement. Pour ajouter un nouveau cas d'erreur : 1. Ajouter le membre dans `ErrorCode`. 2. Le lever via `new HttpError(status, ErrorCode.XXX, "message dev-facing")`. 3. Ajouter sa traduction dans `ErrorMessageService.LABELS.fr`. --- ## Côté API (`apps/api`) - **`lib/http-error.ts`** — `HttpError` : erreur typée portant `status` (code HTTP) et `code` (`ErrorCode`). C'est ce que lèvent les routes/services au lieu de construire une réponse HTTP à la main. - **`services/error-handler.service.ts`** — `ErrorHandlerService` : un seul point qui sait transformer n'importe quelle erreur JS (`ZodError`, `HttpError`, n'importe quoi d'autre) en `{ status, body }`. Le cas générique (`INTERNAL_ERROR`, 500) logue l'erreur côté serveur sans jamais exposer de détail interne au client. - **`app.ts`** — le middleware d'erreur final d'Express ne fait qu'appeler `errorHandlerService.handle(err)` et renvoyer le résultat ; aucune logique de mapping n'y vit directement. ## Côté Web (`apps/web`) - **`api/client.ts`** — `ApiClient` : lève `ApiError` (porteur de `status`, `code`, `fieldErrors`) pour toute réponse non-2xx. - **`services/error-message.service.ts`** — `ErrorMessageService` : associe chaque `ErrorCode` à un libellé, par locale (`Record>`). Une seule langue existe aujourd'hui (`fr`), mais la structure est prête pour en ajouter une deuxième sans toucher aux composants. - Les pages (`LoginPage`, `SignupPage`) attrapent `ApiError`, récupèrent `err.code`, et appellent `errorMessageService.getLabel(err.code)` pour l'afficher — jamais `err.message`. ## Validation côté formulaire (distincte du contrat d'erreurs API) Les schémas zod partagés (`packages/shared/src/schemas/auth.ts`) portent déjà des messages en français, utilisés pour la validation **avant** l'appel réseau (retour instantané, aucun aller-retour serveur). C'est un mécanisme séparé du contrat `ErrorCode` : ces messages ne quittent jamais le navigateur.