Files
_Assistant_Lead_Tech/knowledge/backend/risques/contracts.md
MaksTinyWorkshop 9b7af9f1b0 Refonte Structure
2026-03-25 08:34:19 +01:00

6.3 KiB

Backend — Risques & vigilance : Contracts

Extrait de la base de connaissance Lead_tech. Voir knowledge/backend/risques/README.md pour 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 dans apps/*)
  • En review : repérer les fichiers "config/constants" ajoutés dans apps/* sur des domaines déjà couverts par contracts
  • (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 RequestSchema défini dans packages/contracts mais jamais importé dans le controller ni le service mobile → dead code silencieux qui crée une fausse confiance
  • Un type de retour inline (string brut) à la place du type contracts → désynchronisation silencieuse entre contrat et implémentation

Symptômes

  • grep du nom du schema ne trouve aucun import en dehors de sa définition
  • Service retourne Promise<{ status: string }> au lieu de Promise<CurationResponse> — le status n'est pas validé comme CurationStatus
  • Endpoints POST /action sans 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 :

  1. Chaque RequestSchema est utilisé dans un ZodValidationPipe (API) ou importé dans le service mobile.
  2. Les ResponseSchema correspondent au type de retour typé du service (Promise<TheType>, pas un type inline).
  3. Les endpoints sans body (POST /action) définissent z.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_ERROR ou INTERNAL_ERROR sur 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, jamais VALIDATION_ERROR ou INTERNAL_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

  • ForbiddenException lancé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