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.

5 min read

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.

src/auth/auth.controller.ts
1
import {
2
Body,
3
Controller,
4
Post,
5
} from '@nestjs/common'
6
import { AuthService } from './auth.service'
7
import { CreateUserDto } from './dto/create-user.dto'
8
import { LogUserDto } from './dto/log-user.dto'
9
10
@Controller('auth')
11
export class AuthController {
12
constructor(private readonly authService: AuthService) {}
13
14
@Post('register')
15
async register(
16
@Body() registerBody: CreateUserDto,
17
) {
18
return this.authService.register({ registerBody })
19
}
20
21
@Post('login')
22
async login(@Body() authBody: LogUserDto) {
23
return this.authService.login({ authBody })
24
}
25
}
  • @Controller('auth') : préfixe /auth pour 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

src/auth/dto/create-user.dto.ts
1
import {
2
IsEmail,
3
IsNotEmpty,
4
IsString,
5
MinLength,
6
} from 'class-validator'
7
8
export class CreateUserDto {
9
@IsEmail({}, { message: 'Tu dois fournir une adresse email valide.' })
10
email: string
11
12
@IsNotEmpty()
13
@MinLength(6, {
14
message: 'Ton mot de passe doit faire au moins 6 caractères.',
15
})
16
password: string
17
18
@IsString({ message: 'Le prénom est requis.' })
19
firstName: string
20
}
  • @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

src/auth/dto/log-user.dto.ts
1
import {
2
IsEmail,
3
IsNotEmpty,
4
} from 'class-validator'
5
6
export class LogUserDto {
7
@IsEmail({}, { message: 'Email invalide.' })
8
email: string
9
10
@IsNotEmpty({ message: 'Le mot de passe ne peut pas être vide.' })
11
password: string
12
}

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.

src/main.ts
1
import { ValidationPipe } from '@nestjs/common'
2
import { NestFactory } from '@nestjs/core'
3
import { AppModule } from './app.module'
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule)
7
8
app.useGlobalPipes(
9
new ValidationPipe({
10
whitelist: true,
11
forbidNonWhitelisted: true,
12
}), // ^? Enlève automatiquement les props non déclarées
13
)
14
15
await app.listen(3000)
16
}
17
bootstrap()
  • whitelist: true : supprime les champs non définis dans le DTO.
  • forbidNonWhitelisted: true : renvoie une erreur si des props inattendues sont envoyées.

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 :

src/auth/auth.service.ts
1
import { Injectable, UnauthorizedException } from '@nestjs/common'
2
import { JwtService } from '@nestjs/jwt'
3
import * as bcrypt from 'bcrypt'
4
import { PrismaService } from 'src/prisma/prisma.service'
5
import { CreateUserDto } from './dto/create-user.dto'
6
import { LogUserDto } from './dto/log-user.dto'
7
8
@Injectable()
9
export class AuthService {
10
constructor(
11
private prisma: PrismaService,
12
private jwtService: JwtService,
13
) {}
14
15
async register({ registerBody }: { registerBody: CreateUserDto }) {
16
const { email, password, firstName } = registerBody
17
const existing = await this.prisma.user.findUnique({ where: { email } })
18
if (existing) {
19
throw new UnauthorizedException('Email déjà utilisé.')
20
}
21
const hash = await bcrypt.hash(password, 10)
22
const user = await this.prisma.user.create({
23
data: { email, password: hash, firstName },
24
})
25
const payload = { sub: user.id, email: user.email }
26
return { access_token: this.jwtService.sign(payload) }
27
}
28
29
async login({ authBody }: { authBody: LogUserDto }) {
30
const user = await this.prisma.user.findUnique({
31
where: { email: authBody.email },
32
})
33
if (!user || !(await bcrypt.compare(authBody.password, user.password))) {
34
throw new UnauthorizedException('Identifiants invalides.')
35
}
36
const payload = { sub: user.id, email: user.email }
37
return { 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 JwtService de @nestjs/jwt.

5. Tester tes endpoints

Tu peux utiliser Postman ou Insomnia pour valider :

  1. Envoi d’une requête POST /auth/register avec un corps conforme à CreateUserDto.
  2. Envoi d’une requête POST /auth/login avec un corps conforme à LogUserDto.

Tu devrais recevoir un objet { access_token: "..." } si tout se passe bien.


Exercices rapides

  1. Ajouter un champ lastName dans CreateUserDto :
    • Ajoute la validation @IsString() et teste avec ValidationPipe.
  2. Tester la forbidNonWhitelisted :
    • Envoie un champ inconnu dans ton payload (ex. foo: "bar") et observe l’erreur.
  3. Backlinks
    • Relis la section sur la protection des routes avec JwtAuthGuard pour sécuriser la route GET /auth.