mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-06-28 01:53:40 +02:00
f1b783407a
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>
214 lines
10 KiB
Markdown
214 lines
10 KiB
Markdown
---
|
||
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.
|
||
|
||
---
|
||
|
||
<a id="risque-nestjs-toomanyrequest"></a>
|
||
## 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
---
|
||
|
||
<a id="risque-controller-corrompu-insertions"></a>
|
||
## 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
|
||
|
||
---
|
||
|
||
<a id="risque-repository-dead-layer"></a>
|
||
## 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
|
||
|
||
---
|
||
|
||
<a id="risque-interface-provider-incomplete"></a>
|
||
## 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
|
||
|
||
---
|
||
|
||
<a id="risque-guard-multistatut-read-methods"></a>
|
||
## 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
|
||
|
||
```typescript
|
||
// ❌ 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
|
||
|
||
---
|
||
|
||
<a id="risque-bootstrap-ok-injection-cassee-tsx-watch"></a>
|
||
## 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 --watch` → `tsx 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
|
||
|
||
---
|
||
|
||
<a id="risque-writemodeguard-bloque-endpoints-support"></a>
|
||
## 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
|