Dotychczas walidowaliśmy proste, płaskie obiekty. Ale co jeśli przychodzące dane mają zagnieżdżone obiekty lub tablice obiektów? Potrzebujemy głębokiej inspekcji — strażnicy muszą sprawdzić nie tylko osobę na bramie, ale też jej bagaż, dokumenty w torbie i przedmioty w kieszeniach.
Wyobraź sobie DTO dla legionu, który zawiera listę legionistów:
1// BEZ zagnieżdżonej walidacji
2export class CreateLegioDto {
3 @IsString()
4 name: string;
5
6 @IsArray()
7 soldiers: CreateSoldierDto[]; // Tablica obiektów
8 // Ale czy każdy obiekt w tablicy jest poprawny? NIE WIEMY!
9}Samo
@IsArray() sprawdzi tylko czy wartość jest tablicą — nie zwaliduje zawartości obiektów wewnątrz!Dekorator
@ValidateNested() mówi class-validator: "Sprawdź też walidację obiektu wewnętrznego":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}Ważne:
@ValidateNested() zawsze wymaga @Type() z class-transformer! Bez niego NestJS nie wie, na jaką klasę skonwertować surowy obiekt.Gdy masz tablicę obiektów, musisz dodać
{ each: true }:1export class CreateLegioDto {
2 @IsString()
3 name: string;
4
5 @ValidateNested({ each: true })
6 @Type(() => CreateSoldierDto)
7 soldiers: CreateSoldierDto[];
8}Parametr
{ each: true } mówi dekoratorowi, żeby sprawdził każdy element tablicy osobno. Bez niego walidacja zagnieżdżona nie zadziała na tablicach.Zagnieżdżenie może sięgać wielu poziomów głębokości:
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}W tym przykładzie NestJS zwaliduje:
CreateLegioDto.cohorts to tablica poprawnych CohortDtoCohortDto.soldiers to tablica poprawnych SoldierDtoSoldierDto.primaryWeapon to poprawny WeaponDtoGdy walidacja zagnieżdżona nie przejdzie, NestJS zwróci szczegółowy błąd ze ścieżką do problematycznego pola:
1// Przykładowa odpowiedź błędu:
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}Głęboka inspekcja to kluczowa umiejętność strażników bramy. Bez niej złośliwy użytkownik mógłby przemycić niepoprawne dane w zagnieżdżonych obiektach!