In NestJS, Data Transfer Objects (DTOs) should be defined as classes rather than interfaces to enable runtime metatype access for features like Pipes.

Why classes over interfaces?

Classes are part of JavaScript ES6 and remain as real entities in compiled JavaScript, making them available at runtime.

Interfaces are removed during TypeScript transpilation and don’t exist at runtime.

Example

// ✅ Recommended: Class
export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}
 
// ❌ Avoid: Interface
export interface CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

Runtime metatype access

Pipes (especially validation pipes) require access to the DTO’s metatype at runtime:

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  // ValidationPipe can inspect CreateCatDto class at runtime
}

Only classes provide this capability because interfaces don’t exist after compilation.