Cours NestJS · Module 2: Routing et cycle de requête

Cycle de vie d'une requête dans NestJS - le voyage d'une dépêche à travers l'empire

6 min de lecture
Dans cette leçon5

Architecte de l'empire ! L'architecte Vitruve, maître des bâtiments et des systèmes, a préparé pour toi la leçon la plus importante de ce module. Tu as déjà découvert les middlewares, les guards, les intercepteurs, les pipes, les décorateurs personnalisés et les filtres d'exceptions. Il est maintenant temps de voir comment TOUT cela s'assemble en un seul ensemble - le cycle de vie complet d'une requête HTTP dans NestJS !

Le voyage complet d'une dépêche à travers l'empire

Imagine qu'une dépêche (requête HTTP) parte d'une province lointaine vers César. En chemin, elle passe par de nombreux points de contrôle. Voici l'ordre exact :

1Incoming HTTP request
2        |
3        v
4  1. MIDDLEWARE (Gate guard)
5        |
6        v
7  2. GUARDS (Permission check)
8        |
9        v
10  3. INTERCEPTORS - before (Chronicler - start)
11        |
12        v
13  4. PIPES (Data quality control)
14        |
15        v
16  5. ROUTE HANDLER (Caesar makes the decision)
17        |
18        v
19  6. INTERCEPTORS - after (Chronicler - end)
20        |
21        v
22  7. EXCEPTION FILTERS (Tribunal - in case of error)
23        |
24        v
25  HTTP Response

Chaque étape en détail

1. Middleware - le gardien de la porte

Le middleware s'exécute EN PREMIER, avant même que NestJS ne sache quelle méthode de contrôleur sera appelée. Il a accès aux objets Request et Response bruts :

1// Le middleware s'exécute en PREMIER
2@Injectable()
3export class LoggerMiddleware implements NestMiddleware {
4  use(req: Request, res: Response, next: NextFunction) {
5    console.log('[1. MIDDLEWARE] La dépêche est arrivée à la porte');
6    console.log('Méthode : ' + req.method + ', chemin : ' + req.originalUrl);
7    next(); // Transmets plus loin
8  }
9}

2. Guards - vérification des permissions

Les Guards s'exécutent APRÈS le middleware mais AVANT les intercepteurs et les pipes. Leur seul rôle est de répondre à la question : "Cette requête a-t-elle le droit de continuer ?" :

1// Le guard s'exécute en DEUXIÈME
2@Injectable()
3export class AuthGuard implements CanActivate {
4  canActivate(context: ExecutionContext): boolean {
5    console.log('[2. GUARD] Je vérifie les droits du légionnaire');
6    const request = context.switchToHttp().getRequest();
7    return !!request.headers.authorization; // true = let through, false = block
8  }
9}

3. Intercepteurs (before) - début du chroniqueur

Les intercepteurs ont une propriété unique - ils s'exécutent deux fois : une fois AVANT le handler et une fois APRÈS. La phase "before" est le code situé avant next.handle() :

1// Interceptor - BEFORE phase (third step)
2@Injectable()
3export class TimingInterceptor implements NestInterceptor {
4  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
5    const start = Date.now();
6    console.log('[3. INTERCEPTOR before] Le chroniqueur commence à écrire');
7
8    return next.handle().pipe(
9      // Cette partie est la phase AFTER (sixième étape)
10      tap(() => {
11        const duration = Date.now() - start;
12        console.log('[6. INTERCEPTOR after] Temps : ' + duration + 'ms');
13      }),
14    );
15  }
16}

4. Pipes - contrôle qualité des données

Les Pipes s'exécutent DIRECTEMENT avant le handler. Ils transforment et valident les données d'entrée :

1// Le pipe s'exécute en QUATRIÈME
2@Injectable()
3export class ParseLegionIdPipe implements PipeTransform {
4  transform(value: any) {
5    console.log('[4. PIPE] Je valide les données reçues : ' + value);
6    const id = parseInt(value, 10);
7    if (isNaN(id)) {
8      throw new BadRequestException("L'ID doit être un nombre !");
9    }
10    return id;
11  }
12}

5. Route Handler - la décision de César

Le handler est la méthode cible du contrôleur - c'est là que s'exécute la logique métier :

1@Controller('legiones')
2export class LegionController {
3  @Get(':id')
4  @UseGuards(AuthGuard)
5  @UseInterceptors(TimingInterceptor)
6  findLegion(@Param('id', ParseLegionIdPipe) id: number) {
7    console.log('[5. HANDLER] César traite la demande sur la légion ' + id);
8    return { id, name: 'Legio X Gemina', commander: 'Marcus' };
9  }
10}

6. Intercepteurs (after) - fin du chroniqueur

Une fois le handler terminé, le contrôle revient à l'intercepteur - au code situé à l'intérieur de pipe(tap(...)). Ici, tu peux transformer la réponse, journaliser le temps, etc.

7. Filtres d'exceptions - le tribunal

Les Filtres d'exceptions ne s'exécutent QUE lorsqu'une exception est levée - à n'importe quelle étape. Si un guard lève une ForbiddenException, le filtre l'attrape. Si un pipe lève une BadRequestException - il l'attrape aussi :

1// Exception Filter - attrape les erreurs de CHAQUE étape
2@Catch(HttpException)
3export class ImperiumExceptionFilter implements ExceptionFilter {
4  catch(exception: HttpException, host: ArgumentsHost) {
5    console.log("[7. EXCEPTION FILTER] Le tribunal traite l'erreur");
6    const ctx = host.switchToHttp();
7    const response = ctx.getResponse();
8    response.status(exception.getStatus()).json({
9      error: exception.message,
10      timestamp: new Date().toISOString(),
11    });
12  }
13}

Implications pratiques de l'ordre

Comprendre l'ordre est essentiel pour le débogage :

1// Example: What happens when a guard rejects the request?
2// 1. Middleware - WILL EXECUTE (logging)
3// 2. Guard - WILL REJECT (throws ForbiddenException)
4// 3. Interceptor before - will NOT execute
5// 4. Pipe - will NOT execute
6// 5. Handler - will NOT execute
7// 6. Interceptor after - will NOT execute
8// 7. Exception Filter - WILL EXECUTE (handles ForbiddenException)
9
10// Example: What happens when a pipe rejects the data?
11// 1. Middleware - WILL EXECUTE
12// 2. Guard - WILL EXECUTE (passes through)
13// 3. Interceptor before - WILL EXECUTE
14// 4. Pipe - WILL REJECT (throws BadRequestException)
15// 5. Handler - will NOT execute
16// 6. Interceptor after - tap() et map() ne s'exécutent PAS (pas de réponse), mais l'erreur traverse son flux (catchError la voit)
17// 7. Exception Filter - WILL EXECUTE (handles BadRequestException)

Déboguer le pipeline

Une technique de débogage pratique - ajoute des logs à chaque étape :

1// middleware
2console.log('[MW] ' + req.method + ' ' + req.originalUrl);
3
4// guard
5console.log('[GUARD] User: ' + request.user?.name);
6
7// intercepteur, phase before
8console.log('[INT-B] Handler: ' + context.getHandler().name);
9
10// pipe
11console.log('[PIPE] Value: ' + value + ' -> ' + transformed);
12
13// handler
14console.log('[HANDLER] Processing...');
15
16// intercepteur, phase after
17console.log('[INT-A] Duration: ' + duration + 'ms');
18
19// filtre d'exceptions
20console.log('[FILTER] Error: ' + exception.message);

Quand tu vois les logs, tu peux retracer précisément le voyage de la requête à travers tout le pipeline et trouver rapidement à quelle étape un problème survient.

Résumé du cycle de vie

Retiens cet ordre comme une colonne de légion en marche :

  1. Middleware - premier point de contrôle (requête brute)
  2. Guards - vérification des permissions (laisser passer/refuser)
  3. Intercepteurs before - début de l'observation
  4. Pipes - validation et transformation des données
  5. Handler - logique métier
  6. Intercepteurs after - fin de l'observation, transformation de la réponse
  7. Filtres d'exceptions - gestion des erreurs (quand quelque chose a mal tourné)

Connaître ce cycle de vie est le fondement de tout développeur NestJS avancé - comme connaître la stratégie de bataille pour un centurion !

Code de cette leçon : src/request-lifecycle.ts
1// Cycle de vie d'une requête - Le trajet d'une dépêche à travers l'empire
2console.log("=== CYCLE DE VIE COMPLET D'UNE REQUÊTE dans NestJS ===\n");
3
4// Ordre de traitement d'une requête HTTP :
5const lifecycle = [
6  '1. MIDDLEWARE      - Gardien de la porte (requête brute)',
7  '2. GUARDS          - Contrôle des droits (autoriser/refuser)',
8  '3. INTERCEPTORS    - Chroniqueur - début (before)',
9  '4. PIPES           - Validation et transformation des données',
10  '5. ROUTE HANDLER   - César prend la décision',
11  '6. INTERCEPTORS    - Chroniqueur - fin (after)',
12  '7. EXCEPTION FILTERS - Tribunal (si une erreur survient)',
13];
14
15lifecycle.forEach(step => console.log(step));
16
17console.log("\n=== SCÉNARIOS ===\n");
18
19console.log("Scénario A : Le guard rejette la requête");
20console.log("  1. Middleware    -> EXÉCUTÉ");
21console.log("  2. Guard         -> REJETTE (ForbiddenException)");
22console.log("  3-6.             -> IGNORÉS");
23console.log("  7. Exception Filter -> GÈRE l'erreur\n");
24
25console.log("Scénario B : Le pipe rejette les données");
26console.log("  1. Middleware    -> EXÉCUTÉ");
27console.log("  2. Guard         -> AUTORISE");
28console.log("  3. Interceptor   -> EXÉCUTÉ (before)");
29console.log("  4. Pipe          -> REJETTE (BadRequestException)");
30console.log("  5-6.             -> IGNORÉS");
31console.log("  7. Exception Filter -> GÈRE l'erreur\n");
32
33console.log("Scénario C : Tout est OK");
34console.log("  1. Middleware    -> EXÉCUTÉ");
35console.log("  2. Guard         -> AUTORISE");
36console.log("  3. Interceptor   -> EXÉCUTÉ (before)");
37console.log("  4. Pipe          -> VALIDE les données");
38console.log("  5. Handler       -> TRAITE la logique");
39console.log("  6. Interceptor   -> EXÉCUTÉ (after)");
40console.log("  7. Exception Filter -> non nécessaire\n");
41
42console.log("=== PRINCIPES CLÉS ===");
43console.log("- Le middleware ne connaît pas la méthode cible du contrôleur");
44console.log("- Le guard décide de l'accès (true/false)");
45console.log("- L'intercepteur voit la requête AVANT et APRÈS le handler");
46console.log("- Le pipe valide/transforme DIRECTEMENT avant le handler");
47console.log("- L'Exception Filter attrape les erreurs de CHAQUE étape");
48

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Quel élément de NestJS s'exécute EN PREMIER dans le cycle de vie d'une requête HTTP ?

  2. 2. Qu'est-ce qui distingue les Intercepteurs des autres éléments du pipeline NestJS ?

  3. 3. Que se passe-t-il quand un Guard rejette une requête (retourne false) ? Quels éléments du pipeline ne s'exécuteront PAS ?

  4. 4. Un intercepteur journalise le temps dans tap() après la réponse, et un pipe lève une BadRequestException. Qu'est-ce qui s'exécute encore ?

  5. 5. À quelle étape du cycle de vie d'une requête les Exception Filters peuvent-ils intervenir ?

Tâches pratiques dans le jeu

  • Tri vertical

    Ordonne les étapes du cycle de vie d'une requête HTTP dans NestJS, de la première à la dernière :

  • Éditeur de code

    Écris un LifecycleLoggerMiddleware qui journalise la méthode et le chemin de la requête, puis appelle next()

  • Clique dans l'ordre

    Range l'ordre COMPLET du traitement d'une requête HTTP dans NestJS :

  • Éditeur de code

    Écris un LifecycleAuthGuard qui vérifie l'en-tête authorization - retourne true s'il existe, false sinon

  • Tri horizontal

    Range les éléments pour récupérer l'objet request dans la méthode canActivate d'un guard :

  • Éditeur de code

    Écris un LifecycleInterceptor qui enregistre Date.now() avant next.handle() et qui, dans tap(...) après la réponse, journalise combien de millisecondes a duré le traitement de la requête

  • Tri vertical

    Ordonne ce qui arrive à une requête quand un guard la rejette, de la première étape à la dernière :

  • Éditeur de code

    Écris un LifecycleValidationPipe qui convertit une valeur en nombre et lève une BadRequestException si ce n'est pas un nombre

  • Clique dans l'ordre

    Range les logs de débogage dans l'ordre où ils apparaîtront dans la console pour une requête réussie :