Index des situations
Partez de ce que vous devez faire : chaque ligne donne la réponse courte et mène à l'explication et au code de TaskFlow. Pour un mot-clé précis (un décorateur, un message d'erreur), utilisez aussi la recherche en haut de page.
Démarrer un projet et comprendre l'exécution
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Créer un projet Nest avec Jest, comme dans le plan | nest new dans un terminal, choisir CommonJS + Jest (ESM + Vitest est le défaut, et le seul résultat sans terminal interactif) | J1 · Générer le projet, questions du CLI |
| Comprendre pourquoi une requête lente bloque toutes les autres | Un seul thread JavaScript : tout calcul synchrone bloque l'event loop | Event loop vs thread-per-request |
| Exécuter un calcul CPU lourd sans bloquer l'API | worker_threads ou une file de jobs | Calcul CPU |
| Lancer plusieurs appels I/O en parallèle | Promise.all, pas une boucle d'await | Promise et async/await |
| Démarrer PostgreSQL et Adminer en local | docker compose up -d | Docker Compose |
| Ajouter un module, un contrôleur ou une ressource complète | nest g module, nest g controller, nest g service, nest g resource | Nest CLI, schematics |
| Générer un CRUD complet sans répondre aux questions | nest g resource orders --type rest --crud | Ressource CRUD |
| Retrouver l'alias d'un schematic (guard, interceptor…) | gu, itc, pi, f, mi… ; nest g --help | Schematics et alias |
| Voir ce qu'une commande va créer avant de l'exécuter | -d (--dry-run) | Options de génération |
| Comprendre pourquoi un guard généré ne s'applique pas | Guards, pipes, filters, interceptors et middlewares ne sont pas déclarés : branchez-les | Schematics et alias |
| Partager du code entre plusieurs applications | nest g app, nest g lib (le projet devient un monorepo) | Monorepo |
| Mettre à jour NestJS d'une version majeure | nest info, puis nest upgrade -d | Ajouter et mettre à jour |
| Retrouver où se trouve quoi dans le projet | Un module par domaine, dto/ et entities/ par module | Structure du repo |
Routes, requêtes et réponses
| Vous devez… | Réponse courte | Où |
|---|---|---|
Renvoyer 204 sur un DELETE, 200 sur une action POST | @HttpCode(HttpStatus.NO_CONTENT) / @HttpCode(HttpStatus.OK) | Codes de statut |
Préfixer toutes les routes par /v1 | app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1' }) | Versioning |
Garder /health hors du versioning | @Controller({ path: 'health', version: VERSION_NEUTRAL }) | Health checks |
| Lire un paramètre de chemin UUID et refuser le reste | @Param('id', ParseUUIDPipe) → 400 si invalide | Pipes |
| Filtrer par une valeur d'enum en query | @Query('status', new ParseEnumPipe(TaskStatus, { optional: true })) | Pipes |
| Paginer une liste | PaginationQueryDto (page, limit) + findAndCount / getManyAndCount | Pagination, recherche |
| Récupérer l'utilisateur connecté dans un handler | @CurrentUser() user: AuthUser | @CurrentUser() |
| Fixer un en-tête de réponse sans perdre la sérialisation | @Header() ou un intercepteur, pas @Res() | @Res() dans un contrôleur |
Valider et transformer les entrées
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Valider un corps JSON | DTO classe + décorateurs class-validator + ValidationPipe global | DTO et validation |
| Refuser les champs inconnus | whitelist: true, forbidNonWhitelisted: true | ValidationPipe global |
| Écrire le DTO de mise à jour sans dupliquer | PartialType(CreateXDto), PickType, OmitType | Mapped types |
| Recevoir un nombre ou un booléen en query string | @Type(() => Number), @Transform pour les booléens, transform: true | Pagination et filtre |
| Comprendre pourquoi un type TypeScript ne protège rien | Les types sont effacés à l'exécution | Validation à l'exécution |
| Écrire un pipe de conversion maison | PipeTransform | Pipes |
Injection de dépendances et modules
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Injecter une interface | Un token (Symbol) + @Inject(TOKEN) + provider useClass / useFactory | Tokens, ProjectsStore |
| Choisir une implémentation selon la configuration | Provider useFactory avec inject: [ConfigService, …] | ProjectsStore |
| Utiliser un service d'un autre module | Le module source l'ajoute à exports, le vôtre l'importe | Modules |
| Corriger « Nest can't resolve dependencies of … » | Provider non déclaré, non exporté, ou type de paramètre effacé | Résolution, design:type |
| Configurer un module à partir d'un autre provider | forRootAsync / registerAsync avec inject + useFactory | Modules dynamiques |
| Injecter une valeur qui n'est pas une classe (horloge, constante) | { provide: CLOCK, useValue: … } + @Inject(CLOCK) | CommonModule |
| Charger un module seulement si une variable existe | ConditionalModule.registerWhen(Module, (env) => …) avec une fonction | ConditionalModule |
| Casser une dépendance circulaire | Revoir le découpage ; forwardRef() en dernier recours | forwardRef |
| Garder un état par requête | Jamais dans un singleton ; scope REQUEST en connaissant son coût | Scopes, état dans un singleton |
Base de données et TypeORM
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Brancher PostgreSQL | TypeOrmModule.forRootAsync + forFeature([Entity]) par module | @nestjs/typeorm |
Déclarer une entité avec UUID et dates timestamptz | @PrimaryGeneratedColumn('uuid'), @CreateDateColumn({ type: 'timestamptz' }) | L'entité Project |
Remplacer un findByNameContaining | find({ where: { name: ILike('%…%') } }) ou un QueryBuilder | Recherche |
| Écrire une requête avec jointure | createQueryBuilder().innerJoin(…) | QueryBuilder |
| Supprimer sans perdre la ligne | @DeleteDateColumn + softDelete() | Repository |
| Lier deux entités | @ManyToOne + @JoinColumn, colonne de clé étrangère explicite | Relations, Task |
| Éviter le N+1 | relations ou leftJoinAndSelect | N+1 |
| Faire plusieurs écritures en tout-ou-rien | dataSource.transaction(async (manager) => …), tout passe par manager | Transactions, archiver un projet |
| Contrôler finement une transaction | QueryRunner | QueryRunner |
| Faire évoluer le schéma | migration:generate → relire → migration:run ; jamais synchronize en production | Migrations, synchronize |
| Appliquer les migrations dans l'image Docker | npm run migration:run:prod, étape séparée du démarrage | Docker |
| Remplir une base de développement | npm run seed | Seed |
Erreurs, logs et pipeline de requête
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Savoir dans quel ordre s'exécutent middleware, guards, pipes… | Middleware → Guards → Interceptors → Pipes → Handler → Interceptors → Filters | Ordre d'exécution |
| Uniformiser toutes les réponses d'erreur | Un ExceptionFilter global (APP_FILTER) | AllExceptionsFilter |
| Renvoyer 409 sur un doublon en base | Code PostgreSQL 23505 → CONFLICT dans le filtre | Erreurs TypeORM |
| Renvoyer 404 sur une ressource absente | throw new NotFoundException() | Exceptions HTTP |
| Mesurer la durée de chaque requête | Un NestInterceptor avec tap RxJS | LoggingInterceptor |
| Suivre une requête dans tous les logs | En-tête x-correlation-id + genReqId de nestjs-pino | Corrélation, logs JSON |
| Ne jamais exposer une stack trace ou du SQL | Le filtre renvoie un 500 générique et logue le détail | Filtre global |
Repérer un await oublié | Règle no-floating-promises (oxlint dans le starter Nest 12) | await oublié |
Configuration et cycle de vie
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Avoir un fichier de config par environnement | NODE_ENV + .env.development, .env.test ; seul .env.example est versionné | Profils, fichiers .env |
| Refuser de démarrer avec une configuration invalide | ConfigModule.forRoot({ validate }) | Validation de l'environnement |
| Injecter une configuration typée | registerAs('database', …) + @Inject(databaseConfig.KEY) | Configuration typée |
Comprendre pourquoi PORT reste une chaîne | Écrire le type : PORT: number = 3000 | Métadonnées de type |
| Exécuter du code au démarrage ou à l'arrêt | OnModuleInit, OnApplicationShutdown + enableShutdownHooks() | Cycle de vie |
| Produire des logs JSON en production | nestjs-pino, pino-pretty seulement en développement | Logging |
Sécurité
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Protéger toutes les routes par défaut | JwtAuthGuard global (APP_GUARD) | Guard global |
| Ouvrir une route sans token | @Public() lu par le guard via Reflector | Routes publiques |
| Réserver une route aux admins | @Roles(Role.Admin) + RolesGuard → 403 | RBAC |
| Stocker un mot de passe | argon2id (argon2.hash, argon2.verify) | Hash de mot de passe |
Ne pas renvoyer passwordHash | @Exclude() + ClassSerializerInterceptor (et @ApiHideProperty() pour la doc) | Entités brutes |
| Ne montrer à un utilisateur que ses projets | Filtre par appartenance ; 404 (et non 403) pour les autres | Un utilisateur ne voit que ses projets |
| Limiter les tentatives de connexion | @Throttle({ default: { limit: 5, ttl: 60_000 } }) → 429 | Sécurité |
| Ajouter les en-têtes de sécurité et régler CORS | helmet(), enableCors({ origin }) | Sécurité |
| Prolonger une session sans redemander le mot de passe | Refresh token (non implémenté dans TaskFlow) | Refresh token |
Événements, tâches planifiées, files
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Réagir à un changement sans coupler les services | EventEmitter2.emit() + @OnEvent() | Événement task.status.changed |
| Empêcher l'erreur d'un listener d'être perdue | try/catch + log dans le listener | Erreurs asynchrones |
| Lancer un traitement chaque nuit | @Cron(CronExpression.EVERY_DAY_AT_1AM, { timeZone }) | Cron OVERDUE |
| Calculer « aujourd'hui » dans le bon fuseau | Date calendaire de Paris, pas toISOString() (UTC) | Cron OVERDUE, OVERDUE |
| Rejouer un traitement qui échoue, avec délai | BullMQ : attempts + backoff exponentiel | File BullMQ |
| Faire tourner BullMQ 6 | Installer ioredis explicitement | BullMQ sans ioredis |
Tests
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Tester un service avec ses dépendances simulées | Test.createTestingModule + { provide: X, useValue: mock } | Testing module |
| Simuler un repository TypeORM | { provide: getRepositoryToken(Task), useValue: … } | Mocker un repository |
| Tester l'API de bout en bout | AppModule + supertest + vraie base PostgreSQL | Parcours e2e |
| Isoler la base de test | Service db-test (tmpfs) ou Testcontainers | Base de test |
| Faire échouer la CI sous un seuil de couverture | coverageThreshold dans jest.config.ts | Seuil de couverture |
| Corriger « Reflect.getMetadata is not a function » en test | import 'reflect-metadata' | reflect-metadata |
| Rendre une date « aujourd'hui » testable | Injecter une horloge (CLOCK) | Tests unitaires |
Documentation et mise en production
| Vous devez… | Réponse courte | Où |
|---|---|---|
| Documenter l'API sans annoter chaque champ | Plugin CLI @nestjs/swagger dans nest-cli.json, fichiers *.dto.ts | Swagger |
| Exposer liveness et readiness | Terminus : /health/live sans dépendance, /health/ready avec la base | Health checks |
| Construire une image légère et non root | Dockerfile multi-stage, npm ci --omit=dev, USER node | Dockerfile |
| Lancer l'API conteneurisée en local | Profil Compose app après les migrations | Profils Compose |
| Vérifier chaque branche en CI | Job lint-and-build, tests unitaires, e2e avec un service PostgreSQL | Pipeline GitLab |
| Finir le parcours proprement | Relire LEARNINGS.md, compléter le README | Revue finale |