API NestJS : endpoints register/login et validation DTO
Déclaration des routes d’auth, validation des données, gestion des erreurs et retours structurés.
Dans ce chapitre, tu vas découvrir comment exposer deux routes essentielles dans ton module d’authentification :
- POST /auth/register pour inscrire un nouvel utilisateur
- POST /auth/login pour se connecter et obtenir un JWT
En parallèle, tu verras comment structurer tes DTOs avec class-validator et activer la ValidationPipe globale pour protéger automatiquement ton API.
1. AuthController : définition des endpoints
On commence par le contrôleur qui gère les requêtes HTTP.
Chaque méthode utilise un DTO pour typer et valider le corps de la requête.
1import {2Body,3Controller,4Post,5} from '@nestjs/common'6import { AuthService } from './auth.service'7import { CreateUserDto } from './dto/create-user.dto'8import { LogUserDto } from './dto/log-user.dto'910@Controller('auth')11export class AuthController {12constructor(private readonly authService: AuthService) {}1314@Post('register')15async register(16@Body() registerBody: CreateUserDto,17) {18return this.authService.register({ registerBody })19}2021@Post('login')22async login(@Body() authBody: LogUserDto) {23return this.authService.login({ authBody })24}25}
@Controller('auth'): préfixe/authpour toutes les routes.@Post('register')et@Post('login'): méthodes HTTP dédiées.- Chaque
@Body()est typé par un DTO qui déclenche la validation.
On verra dans la section suivante comment définir ces DTOs.
2. Créer et valider les DTOs avec class-validator
Les DTOs (Data Transfer Objects) centralisent la structure et les règles de validation.
2.1 CreateUserDto
1import {2IsEmail,3IsNotEmpty,4IsString,5MinLength,6} from 'class-validator'78export class CreateUserDto {9@IsEmail({}, { message: 'Tu dois fournir une adresse email valide.' })10email: string1112@IsNotEmpty()13@MinLength(6, {14message: 'Ton mot de passe doit faire au moins 6 caractères.',15})16password: string1718@IsString({ message: 'Le prénom est requis.' })19firstName: string20}
@IsEmail(): vérifie le format d’email.@MinLength(6): impose une longueur minimale sur le mot de passe.- Les messages personnalisés sont renvoyés en cas d’erreur.
2.2 LogUserDto
1import {2IsEmail,3IsNotEmpty,4} from 'class-validator'56export class LogUserDto {7@IsEmail({}, { message: 'Email invalide.' })8email: string910@IsNotEmpty({ message: 'Le mot de passe ne peut pas être vide.' })11password: string12}
3. Activer la ValidationPipe globale
Pour que NestJS applique automatiquement la validation des DTOs, il suffit d’ajouter la ValidationPipe dans le fichier principal de ton app.
1import { ValidationPipe } from '@nestjs/common'2import { NestFactory } from '@nestjs/core'3import { AppModule } from './app.module'45async function bootstrap() {6const app = await NestFactory.create(AppModule)78app.useGlobalPipes(9new ValidationPipe({10whitelist: true,11forbidNonWhitelisted: true,12}), // ^? Enlève automatiquement les props non déclarées13)1415await app.listen(3000)16}17bootstrap()
whitelist: true: supprime les champs non définis dans le DTO.forbidNonWhitelisted: true: renvoie une erreur si des props inattendues sont envoyées.
Pourquoi ?
Cette configuration renforce ta sécurité en ne laissant passer que les données explicitement déclarées dans tes DTOs.
4. AuthService : aperçu des méthodes register/login
On ne va pas entrer dans tous les détails, mais voici la structure générale :
1import { Injectable, UnauthorizedException } from '@nestjs/common'2import { JwtService } from '@nestjs/jwt'3import * as bcrypt from 'bcrypt'4import { PrismaService } from 'src/prisma/prisma.service'5import { CreateUserDto } from './dto/create-user.dto'6import { LogUserDto } from './dto/log-user.dto'78@Injectable()9export class AuthService {10constructor(11private prisma: PrismaService,12private jwtService: JwtService,13) {}1415async register({ registerBody }: { registerBody: CreateUserDto }) {16const { email, password, firstName } = registerBody17const existing = await this.prisma.user.findUnique({ where: { email } })18if (existing) {19throw new UnauthorizedException('Email déjà utilisé.')20}21const hash = await bcrypt.hash(password, 10)22const user = await this.prisma.user.create({23data: { email, password: hash, firstName },24})25const payload = { sub: user.id, email: user.email }26return { access_token: this.jwtService.sign(payload) }27}2829async login({ authBody }: { authBody: LogUserDto }) {30const user = await this.prisma.user.findUnique({31where: { email: authBody.email },32})33if (!user || !(await bcrypt.compare(authBody.password, user.password))) {34throw new UnauthorizedException('Identifiants invalides.')35}36const payload = { sub: user.id, email: user.email }37return { access_token: this.jwtService.sign(payload) }38}39}
- On vérifie d’abord l’existence de l’utilisateur.
- On hash le mot de passe à l’inscription, et on compare avec
bcrypt.compareà la connexion. - Le JWT est généré via
JwtServicede @nestjs/jwt.
5. Tester tes endpoints
Tu peux utiliser Postman ou Insomnia pour valider :
- Envoi d’une requête POST
/auth/registeravec un corps conforme àCreateUserDto. - Envoi d’une requête POST
/auth/loginavec un corps conforme àLogUserDto.
Tu devrais recevoir un objet { access_token: "..." } si tout se passe bien.
Exercices rapides
- Ajouter un champ
lastNamedansCreateUserDto:- Ajoute la validation
@IsString()et teste avec ValidationPipe.
- Ajoute la validation
- Tester la
forbidNonWhitelisted:- Envoie un champ inconnu dans ton payload (ex.
foo: "bar") et observe l’erreur.
- Envoie un champ inconnu dans ton payload (ex.
- Backlinks
- Relis la section sur la protection des routes avec
JwtAuthGuardpour sécuriser la routeGET /auth.
- Relis la section sur la protection des routes avec