Interceptors
Au départ, la route whoAmI dans UsersController fonctionnait ainsi :
- Le contrôleur reçoit la requête
- Il appelle une méthode du
AuthService - Le service lit le cookie / la session pour récupérer un
userId - Le service retrouve l’utilisateur correspondant
Le problème pédagogique/architectural :
- Le contrôleur “sait trop” comment on retrouve l’utilisateur
- On répète la même logique dans plusieurs routes
- On mélange la logique “transversale” (retrouver l’utilisateur courant) avec la logique d’une route
Objectif : éviter de répéter et rendre l’accès à l’utilisateur courant automatique.
Nouvelle approche : @CurrentUser() + CurrentUserInterceptor
L’idée :
- Un interceptor s’exécute avant le contrôleur
- Il récupère le
userIddepuis la session / cookie - Il appelle
UsersService.findOne(userId) - Il ajoute l’utilisateur dans la requête
- Un decorator lit cette valeur dans la requête
- La route reçoit directement l’utilisateur
Résultat : la route devient beaucoup plus simple.
1. Créer le décorateur @CurrentUser()
Le décorateur sert à extraire l’utilisateur depuis la requête.
Il lit simplement une propriété qui sera ajoutée par l’interceptor :
@Get('/whoami')
whoAmI(@CurrentUser() user: User) {
return user;
}Important : le décorateur ne trouve pas l’utilisateur. Il se contente de lire
req.currentUser.
Un décorateur ne peut pas utiliser l’injection de dépendances de NestJS.
Il ne peut donc pas accéder directement aux services comme UsersService.
Cela signifie que le décorateur ne peut pas aller chercher l’utilisateur dans la base de données.
Son rôle est uniquement de lire une valeur déjà présente dans la requête.
2. Créer CurrentUserInterceptor
Le rôle de l’interceptor est de préparer la requête avant qu’elle arrive au contrôleur.
Ce qu’il fait :
- Intercepte la requête
- Lit le
userIddans la session - Si
userIdexiste :- appelle
usersService.findOne(userId)
- appelle
- Ajoute l’utilisateur dans la requête :
req.currentUser = userEnsuite la requête continue vers le contrôleur.
Pourquoi l’interceptor peut accéder à UsersService ?
Parce que l’interceptor est un provider NestJS. Il bénéficie donc de l’injection de dépendances.
3. Appliquer l’interceptor
Option 1 : sur une route
@UseInterceptors(CurrentUserInterceptor)
@Get('/whoami')
whoAmI(@CurrentUser() user: User) {
return user;
}Option 2 : sur un contrôleur
@UseInterceptors(CurrentUserInterceptor)
@Controller('users')
export class UsersController {}Option 3 : globalement
Via un provider global (APP_INTERCEPTOR) dans un module.
Avantage :
Toutes les routes ont accès à @CurrentUser().
Flow complet de la requête
Lorsqu’un client appelle :
GET /users/whoamiLe déroulement est le suivant :
- La requête HTTP arrive avec un cookie / session contenant
userId CurrentUserInterceptorintercepte la requête- Il lit
userId - Il appelle
usersService.findOne(userId) - Il ajoute
req.currentUser = user - Le contrôleur s’exécute
- Le décorateur
@CurrentUser()litreq.currentUser - La route retourne l’utilisateur
En quoi cette approche est meilleure ?
La séparation des responsabilités
- Interceptor → prépare la requête
- Decorator → extrait une valeur de la requête
- Controller → définit les routes
- UsersService → logique CRUD
Lisibilité
Avant : la route devait appeler un service pour retrouver l’utilisateur.
Après :
@Get('/whoami')
whoAmI(@CurrentUser() user: User) {
return user;
}La route est simple et déclarative.
La réutilisabilité
Toutes les routes peuvent accéder à l’utilisateur courant :
updateProfile(@CurrentUser() user: User) {}