Cours NestJS · Module 10: Validation des données
Validation imbriquée - L'inspection en profondeur
Dans cette leçon5
Jusqu'à présent, nous avons validé des objets simples et plats. Mais que se passe-t-il si les données entrantes contiennent des objets imbriqués ou des tableaux d'objets ? Nous avons besoin d'une inspection en profondeur - les gardes doivent vérifier non seulement la personne à la porte, mais aussi ses bagages, les documents dans son sac et les objets dans ses poches.
Le problème - La validation plate ne suffit pas
Imagine un DTO pour une légion qui contient une liste de légionnaires :
1// WITHOUT nested validation
2export class CreateLegioDto {
3 @IsString()
4 name: string;
5
6 @IsArray()
7 soldiers: CreateSoldierDto[]; // Array of objects
8 // But is each object in the array valid? WE DON'T KNOW!
9}@IsArray() à lui seul vérifie uniquement si la valeur est un tableau - il ne validera pas le contenu des objets à l'intérieur !
@ValidateNested() - le garde des objets imbriqués
Le décorateur @ValidateNested() indique à class-validator : « Valide aussi l'objet intérieur » :
1import { ValidateNested } from 'class-validator';
2import { Type } from 'class-transformer';
3
4class CreateSoldierDto {
5 @IsString()
6 @IsNotEmpty()
7 name: string;
8
9 @IsNumber()
10 @Min(16)
11 age: number;
12}
13
14export class CreateLegioDto {
15 @IsString()
16 @IsNotEmpty()
17 name: string;
18
19 @ValidateNested()
20 @Type(() => CreateSoldierDto)
21 commander: CreateSoldierDto;
22}Important : @ValidateNested() requiert toujours @Type() de class-transformer ! Sans lui, NestJS ne sait pas vers quelle classe convertir l'objet brut.
Valider des tableaux d'objets imbriqués
Lorsque tu as un tableau d'objets, tu dois ajouter { each: true } :
1export class CreateLegioDto {
2 @IsString()
3 name: string;
4
5 @ValidateNested({ each: true })
6 @Type(() => CreateSoldierDto)
7 soldiers: CreateSoldierDto[];
8}Le paramètre { each: true } indique au décorateur de vérifier chaque élément du tableau séparément. Sans lui, la validation imbriquée ne fonctionne pas sur les tableaux.
Imbrication multi-niveaux
L'imbrication peut aller sur de nombreux niveaux de profondeur :
1class WeaponDto {
2 @IsString()
3 @IsNotEmpty()
4 name: string;
5
6 @IsNumber()
7 @Min(1)
8 damage: number;
9}
10
11class SoldierDto {
12 @IsString()
13 @IsNotEmpty()
14 name: string;
15
16 @ValidateNested()
17 @Type(() => WeaponDto)
18 primaryWeapon: WeaponDto;
19
20 @IsOptional()
21 @ValidateNested()
22 @Type(() => WeaponDto)
23 secondaryWeapon?: WeaponDto;
24}
25
26class CohortDto {
27 @IsString()
28 name: string;
29
30 @ValidateNested({ each: true })
31 @Type(() => SoldierDto)
32 soldiers: SoldierDto[];
33}
34
35export class CreateLegioDto {
36 @IsString()
37 name: string;
38
39 @ValidateNested({ each: true })
40 @Type(() => CohortDto)
41 cohorts: CohortDto[];
42}Dans cet exemple, NestJS validera :
- Si
CreateLegioDto.cohortsest un tableau deCohortDtovalides - Si chaque
CohortDto.soldiersest un tableau deSoldierDtovalides - Si chaque
SoldierDto.primaryWeaponest unWeaponDtovalide
Messages d'erreur imbriqués
Lorsque la validation imbriquée échoue, NestJS renvoie une erreur détaillée avec le chemin vers le champ problématique :
1// Example error response:
2{
3 "statusCode": 400,
4 "message": [
5 "cohorts.0.soldiers.1.name should not be empty",
6 "cohorts.0.soldiers.1.primaryWeapon.damage must be at least 1"
7 ],
8 "error": "Bad Request"
9}L'inspection en profondeur est une compétence clé pour les gardes de la porte. Sans elle, un utilisateur malveillant pourrait faire passer en contrebande des données invalides à l'intérieur d'objets imbriqués !
Code de cette leçon : src/nested-validation.ts
1// Validation imbriquée - Inspection en profondeur
2import {
3 IsString,
4 IsNotEmpty,
5 IsNumber,
6 Min,
7 Max,
8 IsArray,
9 ArrayMinSize,
10 ValidateNested,
11 IsOptional,
12 IsEnum,
13} from 'class-validator';
14import { Type } from 'class-transformer';
15
16// Weapon DTO
17class WeaponDto {
18 @IsString()
19 @IsNotEmpty()
20 name: string;
21
22 @IsNumber()
23 @Min(1)
24 @Max(100)
25 damage: number;
26}
27
28// TODO : Compléter le DTO du soldat avec la validation imbriquée
29class SoldierDto {
30 @IsString()
31 @IsNotEmpty()
32 name: string;
33
34 @IsNumber()
35 @Min(16)
36 age: number;
37
38 // TODO : Ajouter @ValidateNested() et @Type(() => WeaponDto)
39 primaryWeapon: WeaponDto;
40
41 // TODO : Ajouter @IsOptional(), @ValidateNested(), @Type(() => WeaponDto)
42 secondaryWeapon?: WeaponDto;
43}
44
45// TODO : Compléter le DTO de la cohorte avec un tableau d'objets imbriqués
46class CohortDto {
47 @IsString()
48 @IsNotEmpty()
49 name: string;
50
51 // TODO : Ajouter @ValidateNested({ each: true })
52 // TODO : Ajouter @Type(() => SoldierDto)
53 // TODO : Ajouter @IsArray() et @ArrayMinSize(3)
54 soldiers: SoldierDto[];
55}
56
57console.log('La validation imbriquée vérifie les objets en profondeur !');
58console.log('Rappelle-toi : @ValidateNested requiert @Type !');
59Tu 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. Que fait le décorateur @ValidateNested() dans class-validator ?
2. Pourquoi @ValidateNested() requiert-il @Type() de class-transformer ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Complète les décorateurs de validation imbriquée dans SoldierDto et CohortDto
- Tri vertical
Range les niveaux de validation imbriquée du plus externe au plus interne :
- Clique dans l'ordre
Range les décorateurs du champ soldiers dans CreateLegioDto dans le bon ordre :