Files
_Assistant_Lead_Tech/knowledge/backend/risques/nestjs.md
T
MaksTinyWorkshop f1b783407a docs(knowledge): capitalisation backend — intégration du triage local (mai-juin 2026)
Triage et intégration des propositions backend du buffer 95_a_capitaliser.md
(lot local RL799_V2 + app-alexandrie, mai-juin 2026), distinct de la capitalisation
remote antérieure (triage 2026-05-02).

~73 entrées intégrées sur knowledge/backend/, dont :
- patterns/auth.md : série "membrane d'auth fédérée BFF/OIDC" (9 patterns) + jose algo whitelist
- patterns/prisma.md : recette fusionnée "Migration String/Int → enum" (backfill + Cas A/B/C),
  row réactivable, endpoint replace atomique, updateMany conditionnel, etc.
- risques/general.md : 19 risques (epoch s vs ms, keepAliveTimeout=0, upsert+filtre liste,
  fail-safe catch-all, retrait asymétrique front/back, anti-énumération rate-limit, etc.)
- patterns/general, async, nestjs, contracts, tests + risques/auth, contracts, prisma, redis, stripe, tests
- compléments d'entrées existantes (authorize-after-fetch, P3014, cursor opaque, DI swc, Stripe v20...)
- README patterns/risques mis à jour

Doublons internes corrigés en relecture (suppression-champ .map() → general seul ;
e2e DB-based → tests.md seul). Doublons hors backend / entrées projet / rejets non intégrés.
Source 95_a_capitaliser.md non purgée à ce stade (purge en fin de capitalisation complète).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:25:02 +02:00

10 KiB
Raw Blame History


title: Backend — Risques & vigilance : NestJS domain: backend bucket: risques tags: [nestjs, controllers, guards, providers, review, injection, tsx-watch, mode-degrade] applies_to: [implementation, review, debug] severity: high validated_on: 2026-06-25 source_projects: [app-alexandrie]

Backend — Risques & vigilance : NestJS

Extrait de la base de connaissance Lead_tech. Voir knowledge/backend/risques/README.md pour l'index complet.


NestJS 11 — TooManyRequestsException inexistante

Risques

  • TooManyRequestsException n'est pas exportée par @nestjs/common en NestJS ≥ 11
  • Erreur de compilation ou 500 si utilisée directement

Symptômes

  • Cannot find name 'TooManyRequestsException' à la compilation
  • Test qui passe sur NestJS 10 mais échoue sur 11+

Bonnes pratiques / mitigations

// Pattern sûr pour HTTP 429
throw new HttpException(
  { error: { code: 'QUOTA_EXCEEDED', message: '...' } },
  HttpStatus.TOO_MANY_REQUESTS,
);
  • Contexte technique : NestJS v11+ — 20-03-2026

Controller NestJS corrompu par insertions multiples

Risques

  • Des méthodes imbriquées, décorateurs orphelins ou routes dupliquées cassent la syntaxe TypeScript sans que le compilateur ne l'attrape toujours
  • La story est marquée "completed" alors que le code ne compile pas

Symptômes

  • @Get('/route') apparaît dans le corps d'une autre méthode
  • La même route est déclarée 2-3 fois dans le même controller
  • Erreur NestJS au runtime mais pas à la compilation

Bonnes pratiques / mitigations

  • Quand on ajoute >3 endpoints à un controller existant, réécrire le fichier entier en partant du fichier original

  • Ne jamais insérer par blocs séparés — la concaténation casse la structure AST

  • Checklist review : grep @Get\|@Post\|@Patch\|@Delete dans le controller et vérifier qu'aucune route n'est dupliquée

  • Contexte technique : NestJS / TypeScript — app-alexandrie 20-03-2026


Repository layer non branché (dead layer)

Risques

  • Donner une impression de sécurité alors que le code métier continue d'appeler l'ORM directement
  • Multiplier les chemins d'accès aux données avec des règles différentes
  • Payer le coût d'une abstraction qui n'a aucun effet réel

Symptômes

  • Un repository est créé mais les anciens call sites Prisma restent en place
  • Les nouvelles règles de scoping ou de sécurité ne s'appliquent pas partout
  • La review montre des fichiers de repository peu ou jamais importés

Bonnes pratiques / mitigations

  • Vérifier qu'une nouvelle couche d'abstraction est réellement branchée dans les call sites existants
  • Rechercher explicitement les appels directs restants lors de la review
  • Refuser l'introduction d'une couche repository tant que la migration effective n'est pas faite
  • Contexte technique : TypeScript / Prisma / refactor d'accès aux données — 16-03-2026

Interface provider incomplète ou divergente de ses implémentations

Risques

  • Une implémentation expose des méthodes non déclarées dans le contrat commun
  • Les appelants contournent l'interface et se couplent à un provider concret
  • Une stratégie provider devient non interchangeable en pratique

Symptômes

  • Appels avec cast ou accès direct à une implémentation spécifique
  • Méthodes présentes dans une classe mais absentes de l'interface
  • Régression lors d'un changement de provider

Bonnes pratiques / mitigations

  • Toute capacité commune attendue par les appelants doit être déclarée dans l'interface
  • Interdire les méthodes "cachées" consommées hors contrat
  • Tester au moins une implémentation par le contrat abstrait
  • Contexte technique : TypeScript / provider strategy — 10-03-2026

Guard multi-statut : ordre READ_METHODS avant statut et whitelist des writes critiques

Risques

  • Un guard qui vérifie le statut BLOCKED avant la méthode HTTP bloque aussi les GET, enfermant l'utilisateur sans moyen de sortir (ex : révoquer un appareil).
  • Sans whitelist explicite des writes autorisés en état bloqué, le circuit de sortie est fermé.

Symptômes

  • L'utilisateur avec sessionStatus === 'BLOCKED' ne peut pas accéder à GET /auth/devices
  • Tests passent mais l'ordre des conditions est inversé

Bonnes pratiques / mitigations

// ❌ DANGEREUX — BLOCKED bloque aussi les GET
if (user.sessionStatus === 'BLOCKED') throw new HttpException(...);
if (READ_METHODS.has(request.method)) return true; // jamais atteint pour BLOCKED

// ✅ CORRECT
if (READ_METHODS.has(request.method)) return true;
if (isDeviceManagementPath(request.path)) return true; // whitelist writes critiques
if (user.sessionStatus === 'BLOCKED') throw new HttpException(...);
  • Règle : dans tout guard multi-statut, vérifier READ_METHODS.has(request.method) EN PREMIER, avant tout check de statut.

  • Règle : définir une whitelist explicite des writes autorisés même en état bloqué.

  • Contexte technique : NestJS / guard statut — app-alexandrie 30-03-2026


Bootstrap NestJS OK mais injection runtime cassée sous tsx watch

Risques

  • En dev, un serveur NestJS peut afficher Nest application successfully started et exposer ses routes, tout en cassant à la première requête : des dépendances injectées (Reflector, services consommés par les guards globaux) arrivent à undefined au runtime.
  • Le symptôme initial ressemble à un problème réseau/mobile alors que la cause réelle est l'injection backend.

Symptômes

  • GET / ou une route d'auth répond 500 alors que le bootstrap est nominal
  • Stack runtime du type Cannot read properties of undefined (reading 'getAllAndOverride')
  • Les guards globaux (AuthGuard, guards basés sur Reflector) sont les premiers à tomber

Bonnes pratiques / mitigations

  • Ne pas conclure trop vite à un problème réseau dès que le port écoute : traiter « le port écoute » et « les requêtes fonctionnent » comme deux validations distinctes.
  • Tester immédiatement GET /, l'endpoint OpenAPI et une route publique métier, puis lire la stack runtime après le premier hit (pas seulement les logs de bootstrap).
  • Si un lanceur tsx watch est utilisé avec NestJS, vérifier explicitement la compatibilité avec l'injection runtime ; en cas de doute, expliciter les injections critiques avec @Inject(...) sur guards et services exposés dès le premier hit.

Cause racine : esbuild n'émet pas emitDecoratorMetadata

Tout runner basé sur esbuild (tsx watch, tsup --watch, esbuild-node-runner...) n'implémente pas emitDecoratorMetadata. Sans cette metadata, Nest ne sait plus quels types injecter dans les constructeurs @Injectable() et tombe dans un fallback silencieux : new Service(undefined, undefined, …). Aucun crash au boot ; tous les services à >1 dépendance deviennent inopérants au runtime, le bug n'apparaît qu'au premier appel d'une méthode touchant une dépendance injectée.

Le piège est durable : la CI passe (elle utilise nest build = tsc), les e2e passent (AppModule allégé). Le bug ne se voit qu'au navigateur/mobile, sur les endpoints non couverts par e2e. Cas vécu : introduit par bascule nest start --watchtsx watch src/main.ts (gain de boot / contournement conflit Prisma v7 ESM/CJS), non détecté 2 mois et demi.

Détection en 30 s : console.log({ deps: typeof someDep }) dans le constructor du service qui crashe. Si typeof === 'undefined' au boot alors que le module l'importe → c'est le bug metadata.

Fix recommandé (Nest 11) : nest start --watch --builder swc

  • pnpm add -D @swc/cli @swc/core
  • .swcrc avec jsc.transform.legacyDecorator: true, jsc.transform.decoratorMetadata: true, module.type: "commonjs" (préserve decorators + metadata)
  • nest-cli.json : "compilerOptions": { "builder": "swc", "typeCheck": true } (le typeCheck: true relance tsc --noEmit en parallèle pour garder la sécurité types)
  • package.json : conserver "start:dev": "nest start --watch" (le nest-cli.json prend le relais pour le builder)
  • Si pnpm bloque le binaire natif SWC : onlyBuiltDependencies: ['@swc/core'] dans pnpm-workspace.yaml

Bénéfices : DI nominale, type-check préservé, build ~10× plus rapide que tsc seul (≈190 ms pour 153 fichiers), résout au passage le conflit Prisma v7 ESM/CJS.

Anti-pattern à proscrire : plugin esbuild qui prétend réimplémenter emitDecoratorMetadata (ex : @anatine/esbuild-decorators) — couverture partielle, casse silencieusement sur les types génériques. Sa présence est un signal qu'il faut passer à SWC.

  • Contexte technique : NestJS / tsx watch / esbuild / SWC / injection — app-alexandrie 01-04-2026, complété 26-05-2026

Guard d'écriture en mode dégradé bloque les endpoints de support

Risques

  • Un guard global qui refuse toutes les écritures (POST/PUT/PATCH/DELETE) quand un service critique est down (ex : Redis) bloque aussi les endpoints dont l'utilité est maximale en panne : soumission de ticket de support, feedback, remontée de logs d'erreur client.
  • Cas typique : un endpoint de ticketing (POST /support/...) devient inaccessible précisément au moment où l'utilisateur veut signaler le dysfonctionnement qu'il subit.

Symptômes

  • Soumission de ticket de support impossible quand le service de quota/cache est indisponible
  • Le guard rejette des routes qui devraient rester ouvertes en mode dégradé

Bonnes pratiques / mitigations

  • Lors de l'ajout d'un guard d'écriture en mode dégradé, recenser explicitement les endpoints critiques qui doivent rester disponibles : support, feedback, logs d'erreur client.

  • Whitelister ces préfixes dès la création du guard — pas lors de la code review d'une story ultérieure. Exemple de whitelist : /auth, /billing/.../webhooks, /support.

  • Why : un service de ticketing bloqué quand le backend dysfonctionne est contre-productif — l'utilisateur ne peut pas signaler le problème qu'il est en train de subir.

  • Contexte technique : NestJS / guard mode dégradé — app-alexandrie 01-04-2026