Cours NestJS · Module 10: Validation des données

Validation imbriquée - L'inspection en profondeur

3 min de lecture
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.cohorts est un tableau de CohortDto valides
  • Si chaque CohortDto.soldiers est un tableau de SoldierDto valides
  • Si chaque SoldierDto.primaryWeapon est un WeaponDto valide

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 !');
59

Tu 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. 1. Que fait le décorateur @ValidateNested() dans class-validator ?

  2. 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 :