mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 13:31:43 +02:00
6.3 KiB
6.3 KiB
Backend — Risques & vigilance : Contracts
Extrait de la base de connaissance Lead_tech. Voir
knowledge/backend/risques/README.mdpour l'index complet.
Contrats API implicites (validation faible ou absente)
Risques
- Entrées non validées → erreurs bizarres / vulnérabilités
- Changements qui cassent le front et les intégrations
Symptômes
- 500 sur erreurs utilisateur
- Incohérences de format de réponse
- "Ça marche en staging, pas en prod" (données réelles)
Bonnes pratiques / mitigations
- Schémas (OpenAPI/JSON Schema) + validation serveur
- Formats de réponse cohérents
- Versionner/éviter breaking changes
Erreurs non standardisées (4xx/5xx incohérents)
Risques
- Front et automatisations impossibles à rendre robustes
- Debug long (pas de codes internes, pas de corrélation)
Symptômes
- Clients qui "retry" sur des 4xx
- Messages techniques exposés aux utilisateurs
- Logs inexploitables
Bonnes pratiques / mitigations
- Mapping HTTP standard + format d'erreur stable
- Codes internes d'erreurs applicatives
- requestId/traceId partout
Duplication silencieuse de constantes partagées (contracts) via fichier orphelin
Risques
- Deux sources de vérité qui divergent silencieusement (ex : topics officiels, enums métier, slugs)
- Bug non détecté par TypeScript si la duplication est dans un fichier non importé (code mort)
Symptômes
- Incohérences entre API et client sur des listes/enums "censées être partagées"
- "Ça marche chez moi" selon l'endroit où la constante est importée
- Un fichier de config existe dans
apps/*mais n'est jamais importé/greffé au runtime
Bonnes pratiques / mitigations
- Toute constante partagée vit dans
packages/contracts/src/et est importée depuis là (jamais recopiée dansapps/*) - En review : repérer les fichiers "config/constants" ajoutés dans
apps/*sur des domaines déjà couverts parcontracts - (Optionnel) Outillage : intégrer une étape de détection de code mort / exports inutilisés au CI si ça devient récurrent
Contracts : schema orphelin / type de retour désynchronisé
Risques
- Un
RequestSchemadéfini danspackages/contractsmais jamais importé dans le controller ni le service mobile → dead code silencieux qui crée une fausse confiance - Un type de retour inline (
stringbrut) à la place du type contracts → désynchronisation silencieuse entre contrat et implémentation
Symptômes
grepdu nom du schema ne trouve aucunimporten dehors de sa définition- Service retourne
Promise<{ status: string }>au lieu dePromise<CurationResponse>— lestatusn'est pas validé commeCurationStatus - Endpoints
POST /actionsans body ayant un schema{ pathParam: string }— le param vient du path, pas du body
Bonnes pratiques / mitigations
À chaque story qui ajoute des schemas dans packages/contracts, vérifier en review :
- Chaque
RequestSchemaest utilisé dans unZodValidationPipe(API) ou importé dans le service mobile. - Les
ResponseSchemacorrespondent au type de retour typé du service (Promise<TheType>, pas un type inline). - Les endpoints sans body (
POST /action) définissentz.object({})ou omettent le body schema — ne jamais placer les path params dans le body schema.
// ❌ Anti-pattern — type inline, status non typé
async showcaseThread(...): Promise<{ threadId: string; status: string }> { ... }
// ✅ Pattern correct — type contracts importé
import type { CurationResponse } from '@app-alexandrie/contracts';
async showcaseThread(...): Promise<CurationResponse> { ... }
- Contexte technique : NestJS / Zod / contracts-first — app-alexandrie 23-03-2026
Code d'erreur générique sur statut HTTP sémantique (409 CONFLICT)
Risques
- Utiliser
VALIDATION_ERRORouINTERNAL_ERRORsur un 409 rend les erreurs indistinguables côté client et monitoring - Les clients (mobile, monitoring, tests) ne peuvent pas brancher une logique conditionnelle sans un code sémantique
Symptômes
- Tous les conflits métier remontent le même code → impossible de distinguer "alias déjà résolu" de "handle déjà pris"
- Tests forcés à matcher le message texte au lieu du code → fragiles
Bonnes pratiques / mitigations
Chaque scénario métier distinct doit avoir son propre code dans error-code.ts :
// ❌ Anti-pattern — code générique sur 409
throw new ConflictException({ error: { code: 'VALIDATION_ERROR', message: '...' } });
// ✅ Correct — code sémantique spécifique
throw new ConflictException({ error: { code: 'ALIAS_ALREADY_RESOLVED', message: '...' } });
throw new ConflictException({ error: { code: 'HANDLE_ALREADY_TAKEN', message: '...' } });
-
Règle : 1 scénario métier distinct = 1 code d'erreur distinct
-
Checklist review : tout 409/422 doit avoir un code dans
error-code.ts, jamaisVALIDATION_ERRORouINTERNAL_ERROR -
Contexte technique : NestJS / error-code.ts — app-alexandrie 24-03-2026
ForbiddenException (403) utilisé pour des erreurs de validation
Risques
- Les clients qui filtrent par HTTP 400 manquent les erreurs de validation lancées en 403
- Sémantique API incorrecte → comportements clients imprévisibles
Symptômes
ForbiddenExceptionlancée pour des tags invalides, des formats incorrects, des liens HTTP- Clients API qui ignorent ces erreurs ou les traitent comme des refus d'accès
Bonnes pratiques / mitigations
Tableau de correspondance :
| Cas | Exception correcte | Code HTTP |
|---|---|---|
| Tags invalides, contenu trop long, format incorrect | BadRequestException |
400 |
| Accès refusé explicitement (accès forum, trial read-only) | ForbiddenException |
403 |
| Quota dépassé | HttpException(429) via HttpStatus.TOO_MANY_REQUESTS |
429 |
- Règle : HTTP 403 = "tu n'as pas le droit d'effectuer cette action". HTTP 400 = "ta requête est mal formée".
- Contexte technique : NestJS / HTTP — 20-03-2026