Custom Pipeline Elements — Series 3

Preview — 3 of 10 questions

Why are both decorators needed?

javascript
export class AddressDto {
  @IsString() city: string;
}

export class CreateUserDto {
  @IsString() name: string;

  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}
A@ValidateNested() alone is enough; @Type() is only for the OpenAPI document
B@Type() alone is enough, since it implies nested validation
C@ValidateNested() tells the validator to descend into the property, and @Type() tells the transformer which class to instantiate it as — without the latter, the nested value stays a plain object carrying no constraint metadata, so the descent finds nothing to check
D@Type() converts the nested object to a string before validation

What does @ValidateIf accomplish?

javascript
export class PayoutDto {
  @IsIn(['bank', 'paypal']) method: 'bank' | 'paypal';

  @ValidateIf((o: PayoutDto) => o.method === 'bank')
  @IsString() iban: string;

  @ValidateIf((o: PayoutDto) => o.method === 'paypal')
  @IsEmail() paypalEmail: string;
}
AThe constraints below it are evaluated only when the predicate holds, so one DTO can express a discriminated payload without demanding fields that are irrelevant to the chosen branch
BIt marks the property optional, exactly like @IsOptional()
CIt runs the predicate after validation and rejects the request if it returns false
DIt applies only to nested objects

What does the useContainer call do?

javascript
@ValidatorConstraint({ async: true })
@Injectable()
export class IsEmailAvailable implements ValidatorConstraintInterface {
  constructor(private readonly users: UsersService) {}
  validate(email: string) { return this.users.isEmailAvailable(email); }
}

// main.ts
useContainer(app.select(AppModule), { fallbackOnErrors: true });
AIt replaces Nest's injector with class-validator's own container
BIt registers every DTO as a provider so validation can resolve them
CIt caches validation results between requests
DIt tells class-validator to resolve constraint classes through Nest's container instead of instantiating them itself — which is what allows UsersService to be injected; fallbackOnErrors keeps built-in constraints working when a class is not registered as a provider

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.