NestJS DTO Tutorial – Data Transfer Objects Explained with Examples
As soon as you start accepting request bodies in a NestJS application, you will run into a design question: how should you define the shape of the data you expect to receive? The answer, almost universally in the NestJS ecosystem, is a DTO, or Data Transfer Object.
DTOs are one of those concepts that seem like unnecessary ceremony to beginners, until you see what breaks without them. This lesson explains what a DTO actually is, why it must be a class rather than a plain interface in NestJS, and how to design DTOs for both creating and updating resources.
NestJS DTO: Learning Objectives
- Define what a DTO is and the specific problem it solves.
- Explain why NestJS DTOs are defined as classes, not interfaces.
- Design a CreateDto and an UpdateDto for a realistic resource.
- Understand how DTOs relate to validation decorators covered in the next lesson.
- Apply DTOs consistently across controller methods for type-safe request handling.
NestJS DTO: Key Terms and Definitions
- DTO (Data Transfer Object): A class that defines the exact shape of data expected to be sent to, or returned from, an API endpoint.
- Create DTO: A DTO describing the required and optional fields needed to create a new resource.
- Update DTO: A DTO describing the fields allowed when updating an existing resource, typically making most or all fields optional.
- PartialType: A NestJS utility function that generates a new DTO class with all properties of an existing DTO made optional, commonly used for Update DTOs.
- Type safety: The guarantee, enforced by TypeScript, that a piece of data conforms to a specific, defined structure.
How NestJS DTO Works: Detailed Explanation
Without a formal contract describing what a request body should look like, a controller method accepting @Body() body: any is essentially trusting the client blindly. Any property could be missing, extra, or of the wrong type, and TypeScript would offer no protection at all, since the any type disables type checking entirely. This is precisely the problem DTOs solve.
A DTO is simply a class describing the exact shape of expected data. Instead of @Body() body: any, you write @Body() createUserDto: CreateUserDto, where CreateUserDto is a class explicitly declaring properties like name: string and email: string. This immediately gives you TypeScript's compile-time safety: typos in property names, wrong types, and missing required fields become visible errors during development rather than confusing runtime bugs in production.
A crucial detail is that NestJS DTOs must be defined as classes, not interfaces, even though interfaces might feel like the more 'correct' TypeScript tool for describing shape-only data. This is because interfaces are erased completely at compile time and have no runtime representation, but NestJS's validation system (covered in depth in the next lesson) relies on decorators attached to class properties, and decorators require an actual class to attach metadata to. An interface simply cannot carry that decorator metadata into the compiled JavaScript.
In most real applications, you will define at least two DTOs per resource: a CreateDto capturing every field required to create a brand-new record, and an UpdateDto capturing the fields a client is allowed to modify. Since updates are typically partial, meaning a client might only want to change one field out of many, NestJS provides a handy utility called PartialType (from @nestjs/mapped-types or @nestjs/swagger) that automatically generates an UpdateDto from an existing CreateDto, making every property optional without duplicating the entire class definition by hand.
DTOs also serve as living documentation. Any developer opening a controller file can immediately see, just from the DTO's property list, exactly what data a given endpoint expects, without needing to read through the entire implementation or guess based on scattered validation logic.
Interview-Friendly Explanation
A strong interview or viva answer for nestjs dto must go beyond a one-line definition. Interviewers evaluating BCA, MCA, B.Tech, and self-taught backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters in production Node.js applications. Use the four-point framework below when answering under time pressure.
- Definition point: DTO (Data Transfer Object): A class that defines the exact shape of data expected to be sent to, or returned from, an API endpoint.
- Working point: Explain why NestJS DTOs are defined as classes, not interfaces.
- Example point: Every production NestJS API you will encounter defines a dedicated CreateDto and UpdateDto for each resource, forming a consistent, predictable convention across the entire codebase.
- Conclusion point: DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.
How to Answer This in a Technical Interview
- Give a two-to-three sentence definition using correct NestJS and Node.js terminology.
- Add one specific example drawn from a real backend scenario such as an e-commerce, fintech, or SaaS API.
- Mention the request lifecycle stage this concept operates at (pipe, guard, middleware, filter, interceptor) since interviewers often test whether you know the execution order.
- Close with one benefit, trade-off, or production use case.
- Avoid vague answers like "it just validates stuff" — interviewers filter these out immediately.
Practical Scenario
Imagine you are hardening a production API for a SaaS product, similar to systems used at companies like Razorpay, Freshworks, or Postman. Every lesson in this module maps directly to a decision you will make while making that backend secure, predictable, and resilient to bad input: validating what comes in, transforming it safely, catching failures gracefully, and protecting routes from unauthorized access.
NestJS DTO: Architecture and Flow Diagram
Visualize a simple relationship diagram:
[CreateUserDto] --(all fields required)--> name: string, email: string, password: string
|
| PartialType()
v
[UpdateUserDto] --(all fields optional)--> name?: string, email?: string, password?: string
Below: 'A client's request body --> validated and shaped against a DTO class --> passed safely into the controller method.'
NestJS DTO: Aspect Reference Table
| Aspect | Using 'any' Type | Using a DTO Class |
|---|---|---|
| Type safety | None, any bypasses TypeScript checks | Full compile-time type checking |
| Documentation | Implicit, must read implementation to understand shape | Explicit, shape is declared directly in the DTO class |
| Validation support | Not possible without extra manual code | Works directly with class-validator decorators |
| Refactoring safety | Risky, no compiler warnings on shape changes | Safe, compiler flags mismatches immediately |
NestJS DTO: NestJS Code Example
// dto/create-user.dto.ts
export class CreateUserDto {
name: string;
email: string;
password: string;
age?: number; // optional even in the Create DTO
}
// dto/update-user.dto.ts — reuses CreateUserDto with all fields made optional
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}
// UpdateUserDto now has: name?: string, email?: string, password?: string, age?: number
// users.controller.ts — using both DTOs
import { Controller, Post, Patch, Param, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
return { message: 'User created', data: createUserDto };
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return { message: `User ${id} updated`, changes: updateUserDto };
}
}
CreateUserDto declares the full shape required to create a new user, with age marked optional using the ? syntax. UpdateUserDto then reuses this same class through PartialType(CreateUserDto), automatically generating a version where every field, including name, email, and password, becomes optional, since an update might only touch one field. This avoids duplicating the entire class definition while keeping both DTOs perfectly in sync whenever CreateUserDto changes. The controller then simply types its @Body() parameters against these DTOs, gaining full TypeScript safety and, once validation is added in the next lesson, automatic runtime enforcement too.
Real-World NestJS DTO Industry Examples
- Every production NestJS API you will encounter defines a dedicated CreateDto and UpdateDto for each resource, forming a consistent, predictable convention across the entire codebase.
- API documentation tools like Swagger, when integrated with NestJS, automatically generate request body schemas directly from DTO class definitions, saving teams from writing documentation manually.
- Frontend and backend teams often share a contract by generating TypeScript types for the frontend directly from backend DTOs, ensuring both sides agree on the exact shape of data being exchanged.
- Payment and fintech APIs rely heavily on strict DTOs to ensure that critical fields like amount and currency are never accidentally omitted or mistyped before reaching business logic.
NestJS DTO Interview Questions and Answers
Q1. What is a DTO and why is it used in NestJS applications?
Short answer: A DTO, or Data Transfer Object, is a class that defines the exact expected shape of data sent to or from an API endpoint. It is used to bring type safety, clear documentation, and validation support to request handling, replacing unsafe patterns like typing a request body as 'any'.
Detailed explanation: Without a formal contract describing what a request body should look like, a controller method accepting @Body() body: any is essentially trusting the client blindly. Any property could be missing, extra, or of the wrong type, and TypeScript would offer no protection at all, since the any type disables type checking entirely. This is precisely the problem DTOs solve. A DTO is simply a class describing the exact shape of expected data. Instead of @Body() body: any, you write @Body() createUserDto: CreateUserDto, where CreateUserDto is a class explicitly declaring properties like name: string and email: string. This immediately gives you TypeScript's compile-time safety: typos in property names, wrong types, and missing required fields become visible errors during development rather than confusing runtime bugs in production. A crucial detail is that NestJS DTOs must be defined as classes, not interfaces, even though interfaces might feel like the more 'correct' TypeScript tool for describing shape-only data. This is because interfaces are erased completely at compile time and have no runtime representation, but NestJS's validation system (covered in depth in the next lesson) relies on decorators attached to class properties, and decorators require an actual class to attach metadata to. An interface simply cannot carry that decorator metadata into the compiled JavaScript. In most real applications, you will define at least two DTOs per resource: a CreateDto capturing every field required to create a brand-new record, and an UpdateDto capturing the fields a client is allowed to modify. Since updates are typically partial, meaning a client might only want to change one field out of many, NestJS provides a handy utility called PartialType (from @nestjs/mapped-types or @nestjs/swagger) that automatically generates an UpdateDto from an existing CreateDto, making every property optional without duplicating the entire class definition by hand. DTOs also serve as living documentation. Any developer opening a controller file can immediately see, just from the DTO's property list, exactly what data a given endpoint expects, without needing to read through the entire implementation or guess based on scattered validation logic.
Practical example: Every production NestJS API you will encounter defines a dedicated CreateDto and UpdateDto for each resource, forming a consistent, predictable convention across the entire codebase.
Interview tip: DTO = Data Transfer Object, defined as a class (not an interface) so decorators can attach validation metadata.
Revision hook: DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.
Q2. Why must NestJS DTOs be classes instead of TypeScript interfaces?
Short answer: Interfaces exist only at compile time and are completely erased from the compiled JavaScript output, meaning there is nothing at runtime for a decorator to attach metadata to. Classes, however, do exist at runtime, allowing validation decorators like @IsString() or @IsEmail() to be attached to their properties and later read by NestJS's validation pipeline.
Detailed explanation: CreateUserDto declares the full shape required to create a new user, with age marked optional using the ? syntax. UpdateUserDto then reuses this same class through PartialType(CreateUserDto), automatically generating a version where every field, including name, email, and password, becomes optional, since an update might only touch one field. This avoids duplicating the entire class definition while keeping both DTOs perfectly in sync whenever CreateUserDto changes. The controller then simply types its @Body() parameters against these DTOs, gaining full TypeScript safety and, once validation is added in the next lesson, automatic runtime enforcement too.
Practical example: API documentation tools like Swagger, when integrated with NestJS, automatically generate request body schemas directly from DTO class definitions, saving teams from writing documentation manually.
Interview tip: CreateDto typically has required fields; UpdateDto typically has optional fields via PartialType.
Revision hook: The class-over-interface requirement is not arbitrary; it is a direct consequence of how TypeScript decorators work.
Q3. How would you create an UpdateDto without duplicating an existing CreateDto?
Short answer: You can use NestJS's PartialType utility, typically imported from @nestjs/mapped-types, extending it with the existing CreateDto as its argument, for example class UpdateUserDto extends PartialType(CreateUserDto). This automatically generates a new class with all the same properties, but all marked optional.
Detailed explanation: A DTO, or Data Transfer Object, is a class that explicitly defines the shape of data expected by an API endpoint, replacing unsafe patterns like typing request bodies as 'any'. NestJS DTOs must be classes rather than interfaces because decorators, which power the validation system covered in the next lesson, require a real runtime construct to attach metadata to. Most resources define a CreateDto describing required fields for new records, and an UpdateDto, often generated using NestJS's PartialType utility, making those same fields optional for partial updates. DTOs bring type safety, prevent duplicated field definitions, and act as clear, self-documenting contracts for exactly what data each endpoint expects.
Practical example: Frontend and backend teams often share a contract by generating TypeScript types for the frontend directly from backend DTOs, ensuring both sides agree on the exact shape of data being exchanged.
Interview tip: PartialType comes from @nestjs/mapped-types (or @nestjs/swagger) and avoids duplicating field definitions.
Revision hook: PartialType is the standard, DRY way to derive an UpdateDto from an existing CreateDto.
Q4. What is the practical difference between a CreateDto and an UpdateDto?
Short answer: A CreateDto typically requires all mandatory fields needed to construct a brand-new resource, since nothing exists yet to fall back on. An UpdateDto usually makes most or all fields optional, since a client updating a resource may only want to change one or two properties without resubmitting the entire object.
Detailed explanation: Without a formal contract describing what a request body should look like, a controller method accepting @Body() body: any is essentially trusting the client blindly. Any property could be missing, extra, or of the wrong type, and TypeScript would offer no protection at all, since the any type disables type checking entirely. This is precisely the problem DTOs solve. A DTO is simply a class describing the exact shape of expected data. Instead of @Body() body: any, you write @Body() createUserDto: CreateUserDto, where CreateUserDto is a class explicitly declaring properties like name: string and email: string. This immediately gives you TypeScript's compile-time safety: typos in property names, wrong types, and missing required fields become visible errors during development rather than confusing runtime bugs in production. A crucial detail is that NestJS DTOs must be defined as classes, not interfaces, even though interfaces might feel like the more 'correct' TypeScript tool for describing shape-only data. This is because interfaces are erased completely at compile time and have no runtime representation, but NestJS's validation system (covered in depth in the next lesson) relies on decorators attached to class properties, and decorators require an actual class to attach metadata to. An interface simply cannot carry that decorator metadata into the compiled JavaScript. In most real applications, you will define at least two DTOs per resource: a CreateDto capturing every field required to create a brand-new record, and an UpdateDto capturing the fields a client is allowed to modify. Since updates are typically partial, meaning a client might only want to change one field out of many, NestJS provides a handy utility called PartialType (from @nestjs/mapped-types or @nestjs/swagger) that automatically generates an UpdateDto from an existing CreateDto, making every property optional without duplicating the entire class definition by hand. DTOs also serve as living documentation. Any developer opening a controller file can immediately see, just from the DTO's property list, exactly what data a given endpoint expects, without needing to read through the entire implementation or guess based on scattered validation logic.
Practical example: Payment and fintech APIs rely heavily on strict DTOs to ensure that critical fields like amount and currency are never accidentally omitted or mistyped before reaching business logic.
Interview tip: DTOs serve as both a type-safety mechanism and a form of self-documentation for API endpoints.
Revision hook: Every serious NestJS resource should have at least a CreateDto and, in most cases, an UpdateDto.
NestJS DTO MCQs and Practice Questions
1. What does DTO stand for in the context of NestJS?
- Data Type Object
- Data Transfer Object
- Database Table Object
- Direct Transfer Operation
Answer: B. Data Transfer Object
Explanation: DTO stands for Data Transfer Object, a design pattern used to define the exact structure of data being sent between a client and a server.
Concept link: DTO = Data Transfer Object, defined as a class (not an interface) so decorators can attach validation metadata.
Why this matters: DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.
2. Why can't a plain TypeScript interface be used with NestJS's validation decorators?
- Interfaces are too slow
- Interfaces are erased at compile time and have no runtime representation for decorators to attach to
- Interfaces cannot have optional properties
- NestJS does not support TypeScript interfaces at all
Answer: B. Interfaces are erased at compile time and have no runtime representation for decorators to attach to
Explanation: Decorators require an actual runtime construct to attach metadata to, and since interfaces are purely compile-time constructs with no JavaScript output, they cannot host decorator metadata the way classes can.
Concept link: CreateDto typically has required fields; UpdateDto typically has optional fields via PartialType.
Why this matters: The class-over-interface requirement is not arbitrary; it is a direct consequence of how TypeScript decorators work.
3. Which NestJS utility generates a DTO with all properties made optional from an existing DTO?
- OmitType
- PickType
- PartialType
- IntersectionType
Answer: C. PartialType
Explanation: PartialType takes an existing DTO class and returns a new class with all of its properties marked as optional, commonly used to build UpdateDto classes from an existing CreateDto.
Concept link: PartialType comes from @nestjs/mapped-types (or @nestjs/swagger) and avoids duplicating field definitions.
Why this matters: PartialType is the standard, DRY way to derive an UpdateDto from an existing CreateDto.
4. What is a key benefit of using a CreateUserDto instead of typing a request body as 'any'?
- It makes the API faster
- It provides compile-time type safety and clearer documentation of expected data
- It automatically connects to a database
- It removes the need for a controller
Answer: B. It provides compile-time type safety and clearer documentation of expected data
Explanation: A DTO class gives TypeScript enough information to catch shape mismatches at compile time and gives other developers a clear, explicit contract of what data an endpoint expects, unlike the untyped 'any' type.
Concept link: DTOs serve as both a type-safety mechanism and a form of self-documentation for API endpoints.
Why this matters: Every serious NestJS resource should have at least a CreateDto and, in most cases, an UpdateDto.
Common NestJS DTO Mistakes to Avoid
- Defining DTOs as interfaces instead of classes, which silently breaks validation since decorators cannot attach metadata to interfaces.
- Duplicating an entire DTO's fields by hand to create an UpdateDto instead of using PartialType, leading to two definitions that can drift out of sync.
- Making every field in a CreateDto optional out of convenience, defeating the purpose of enforcing which fields are genuinely required to create a resource.
- Reusing the same DTO for both request input and database entity shape, blurring the boundary between what a client can send and what is actually stored.
NestJS DTO: Interview Notes and Exam Tips
- DTO = Data Transfer Object, defined as a class (not an interface) so decorators can attach validation metadata.
- CreateDto typically has required fields; UpdateDto typically has optional fields via PartialType.
- PartialType comes from @nestjs/mapped-types (or @nestjs/swagger) and avoids duplicating field definitions.
- DTOs serve as both a type-safety mechanism and a form of self-documentation for API endpoints.
Key NestJS DTO Takeaways
- DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.
- The class-over-interface requirement is not arbitrary; it is a direct consequence of how TypeScript decorators work.
- PartialType is the standard, DRY way to derive an UpdateDto from an existing CreateDto.
- Every serious NestJS resource should have at least a CreateDto and, in most cases, an UpdateDto.
NestJS DTO: Summary
A DTO, or Data Transfer Object, is a class that explicitly defines the shape of data expected by an API endpoint, replacing unsafe patterns like typing request bodies as 'any'. NestJS DTOs must be classes rather than interfaces because decorators, which power the validation system covered in the next lesson, require a real runtime construct to attach metadata to. Most resources define a CreateDto describing required fields for new records, and an UpdateDto, often generated using NestJS's PartialType utility, making those same fields optional for partial updates. DTOs bring type safety, prevent duplicated field definitions, and act as clear, self-documenting contracts for exactly what data each endpoint expects.