Controllers, DTOs and Validation

Why are DTOs classes and not interfaces in NestJS?

Interfaces are erased at compile time. Classes exist at runtime, so they can carry class-validator decorators, be instantiated by class-transformer (transform: true), and be read by Swagger. ValidationPipe uses the parameter's class (from metadata) to know which rules to apply.

Explain ValidationPipe options whitelist, forbidNonWhitelisted and transform.

  • whitelist: true: strips properties that have no validation decorator (mass-assignment protection, e.g. a client sending role: "admin").
  • forbidNonWhitelisted: true: rejects the request with 400 instead of silently stripping.
  • transform: true: converts the plain body into a DTO instance and converts primitive params/query to the declared types ("7" to 7), together with @Type(() => Number) or enableImplicitConversion.

How do you validate nested objects and arrays?

export class CreateOrderDto {
  @ValidateNested({ each: true })      // validate every element
  @Type(() => OrderItemDto)            // tell class-transformer the element class
  @ArrayMinSize(1)
  items: OrderItemDto[];

  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}

Without @Type, the nested values stay plain objects and their decorators are never checked.

How do you create an UpdateDto without copying all fields?

Use mapped types: class UpdateProductDto extends PartialType(CreateProductDto) {}. They copy the properties and validation decorators and make them optional. PickType, OmitType and IntersectionType also exist. Import them from @nestjs/swagger if you use Swagger (so docs stay correct), otherwise from @nestjs/mapped-types.