routing-controllers : @JsonController, @Post, @Get, Modules
Bootcode IWA-S04 — Semaine 20, Jour 3
1. Savoir lire un controller routing-controllers
Comprendre les décorateurs @JsonController, @Post, @Get, @Body, @Param
2. Identifier les endpoints et leurs méthodes HTTP
Chaque décorateur correspond à une méthode HTTP et une route
3. Comprendre le rĂ´le des middlewares (auth, rate limiter)
@UseBefore protège les routes avec AuthenticationMiddleware
4. Voir comment les use cases sont injectés dans les controllers
Le controller reçoit ses use cases par injection Inversify
routing-controllers : des décorateurs TypeScript
Qui génèrent des routes Express automatiquement
Les décorateurs essentiels
@JsonController, @Post, @Get, @Body(), @Param()
@UseBefore — protéger les routes
AuthenticationMiddleware, rate limiter
Lire un vrai controller : UserController
Comprendre chaque ligne du codebase
Le pattern Module
La classe qui configure le DI pour le controller
Express avec des décorateurs TypeScript
« Plus de app.post() manuel. Les décorateurs génèrent les routes Express pour toi. »
❌ Express manuel
app.post('/users/signup', async (req, res) => {
const { email, password } = req.body;
const user = await signUp.execute({ email, password });
res.json(user);
});
Manuel, répétitif, pas de validation auto.
âś… routing-controllers
@JsonController('/users')
export class UserController {
@Post('/signup')
signup(@Body() body: SignUpCommand) {
return this.signUp.execute(body);
}
}
Déclaratif, validation auto, injection DI.
Chaque décorateur a un rôle précis.
@JsonController('/users')
Déclare la classe comme controller. Préfixe toutes les routes avec /users. Réponses en JSON automatiques.
@Post('/signup')
Crée une route POST /users/signup. Aussi : @Get, @Put, @Delete, @Patch.
@Body() body: SignUpCommand
Extrait et valide le body de la requête. Le type est un DTO validé par class-validator.
@Param('id') id: string
Extrait un paramètre d'URL. /users/:id → @Param('id').
Les DTOs utilisent des décorateurs de validation. routing-controllers valide avant d'appeler la méthode.
export class SignUpCommand {
@IsEmail()
email: string;
@IsNotEmpty()
@MinLength(8)
password: string;
}
// Si la validation échoue → 400 Bad Error automatiquement
// La méthode du controller n'est jamais appelée
💡 Le controller ne fait AUCUNE validation manuelle. class-validator protège l'API automatiquement.
Protéger les routes avec un middleware
« @UseBefore(AuthenticationMiddleware) vérifie le JWT avant d'exécuter la méthode. »
Au niveau route
@Get('/me')
@UseBefore(AuthMiddleware)
me(@Req() req: Request) {
return req.user;
}
Seule cette route est protégée.
Au niveau classe
@JsonController('/admin')
@UseBefore(AuthMiddleware, AdminGuard)
export class AdminController { ... }
Toutes les routes du controller sont protégées.
Lire et comprendre chaque ligne d'un vrai controller.
@JsonController('/users')
@injectable()
export class UserController {
constructor(
@inject(TYPES.SignUpUseCase) private signUp: SignUp,
@inject(TYPES.SignInUseCase) private signIn: SignIn,
) {}
@Post('/signup')
async signup(@Body() cmd: SignUpCommand) {
return this.signUp.execute(cmd);
}
@Post('/signin')
async signin(@Body() cmd: SignInCommand) {
return this.signIn.execute(cmd);
}
}
💡 Le controller ne fait qu'orchestrer : il reçoit la commande, appelle le use case, retourne le résultat. Zéro logique métier.
Ne pas confondre — le controller délègue, le use case exécute.
Controller
Route HTTP. Reçoit la requête, valide, appelle le use case, formate la réponse.
Aucune logique métier.
Use Case
Logique métier. Orchestre l'agrégat, le repository, les événements.
Aucune connaissance d'HTTP.
⚠️ Piège : mettre de la logique métier dans le controller. Le controller doit rester mince.
La classe qui configure le DI pour le controller — le dernier maillon de la chaîne.
export class UserModule implements Module {
configure(container: Container) {
// Enregistrer le controller
container.bind(UserController).toSelf();
// Enregistrer les use cases qu'il utilise
container.bind(TYPES.SignUpUseCase).to(SignUp);
container.bind(TYPES.SignInUseCase).to(SignIn);
// Enregistrer les repositories
container.bind(TYPES.UserRepository).to(PostgresUserRepository);
}
}
💡 Le Module regroupe tout le câblage d'un domaine : controller + use cases + repositories. C'est le dernier maillon de la chaîne Build → Module → Controller.
❌ Confondre le controller et le use case
Le controller (route HTTP) délègue. Le use case (logique métier) exécute. Ne pas mettre de métier dans le controller.
⚠️ Oublier les DTOs et la validation
Toujours utiliser un DTO avec class-validator. Sinon l'API accepte n'importe quoi.
❌ Penser que les décorateurs sont magiques
Ils génèrent du code Express standard. @Post('/signup') = app.post('/users/signup', ...).
✅ routing-controllers = Express avec des décorateurs TypeScript — plus de app.post() manuel.
✅ Le controller reçoit les use cases par injection (Inversify) — il ne fait qu'orchestrer.
✅ class-validator valide les DTOs automatiquement — @IsNotEmpty(), @IsEmail(), etc.
✅ Un Module configure le DI pour son controller — c'est le dernier maillon de la chaîne.
Demain : les événements asynchrones 🚀