Lesson 12 of 5020 min read

NestJS DTO Tutorial – Data Transfer Objects Explained with Examples

Learn what DTOs are in NestJS, why they matter for validation and type safety, and how to design clean Create and Update DTOs for real APIs.

Author: CodersNexus

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

  1. Give a two-to-three sentence definition using correct NestJS and Node.js terminology.
  2. Add one specific example drawn from a real backend scenario such as an e-commerce, fintech, or SaaS API.
  3. Mention the request lifecycle stage this concept operates at (pipe, guard, middleware, filter, interceptor) since interviewers often test whether you know the execution order.
  4. Close with one benefit, trade-off, or production use case.
  5. 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

AspectUsing 'any' TypeUsing a DTO Class
Type safetyNone, any bypasses TypeScript checksFull compile-time type checking
DocumentationImplicit, must read implementation to understand shapeExplicit, shape is declared directly in the DTO class
Validation supportNot possible without extra manual codeWorks directly with class-validator decorators
Refactoring safetyRisky, no compiler warnings on shape changesSafe, 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?

  1. Data Type Object
  2. Data Transfer Object
  3. Database Table Object
  4. 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?

  1. Interfaces are too slow
  2. Interfaces are erased at compile time and have no runtime representation for decorators to attach to
  3. Interfaces cannot have optional properties
  4. 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?

  1. OmitType
  2. PickType
  3. PartialType
  4. 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'?

  1. It makes the API faster
  2. It provides compile-time type safety and clearer documentation of expected data
  3. It automatically connects to a database
  4. 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.

Frequently Asked Questions

No. A DTO describes the shape of data exchanged with clients over the API, while a database entity or model describes how data is structured and stored in the database. They often share similar fields but serve different purposes and can diverge, for example a DTO might exclude a password hash that the entity stores. In interviews, tie this back to: DTO = Data Transfer Object, defined as a class (not an interface) so decorators can attach validation metadata. In real applications, consider this 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. Key revision takeaway: DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.

Not necessarily every method, but you typically need at least one DTO per distinct data shape your API accepts, commonly a CreateDto and an UpdateDto per resource. Read-only endpoints like a simple GET by ID usually do not need a dedicated DTO since they don't accept a body. In interviews, tie this back to: CreateDto typically has required fields; UpdateDto typically has optional fields via PartialType. In real applications, consider this 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. Key revision takeaway: The class-over-interface requirement is not arbitrary; it is a direct consequence of how TypeScript decorators work.

Yes. A DTO can mix required properties (declared normally, like name: string) and optional properties (declared with a question mark, like age?: number) within the same class, which is common in CreateDto classes with a few optional fields. In interviews, tie this back to: PartialType comes from @nestjs/mapped-types (or @nestjs/swagger) and avoids duplicating field definitions. In real applications, consider this 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. Key revision takeaway: PartialType is the standard, DRY way to derive an UpdateDto from an existing CreateDto.

Your code will still run, but you lose all compile-time type checking on that data, meaning typos, missing fields, or wrong types will not be caught until they cause a runtime error, and you also lose the ability to easily apply NestJS's automatic validation pipeline. In interviews, tie this back to: DTOs serve as both a type-safety mechanism and a form of self-documentation for API endpoints. In real applications, consider this 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. Key revision takeaway: Every serious NestJS resource should have at least a CreateDto and, in most cases, an UpdateDto.

No, you can always write a separate UpdateDto class manually with its own optional fields, which is useful if the update shape genuinely differs from the create shape. PartialType is simply the most common, DRY approach when the update fields are a strict subset of the create fields. In interviews, tie this back to: DTO = Data Transfer Object, defined as a class (not an interface) so decorators can attach validation metadata. In real applications, consider this 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. Key revision takeaway: DTOs replace vague, unsafe 'any' typed request bodies with explicit, self-documenting class definitions.

No. While a CreateUserDto might legitimately accept a password field as input, you should never return that same DTO shape (or the raw entity) directly in a response; instead, use a separate response DTO or serialization strategy that explicitly excludes sensitive fields. In interviews, tie this back to: CreateDto typically has required fields; UpdateDto typically has optional fields via PartialType. In real applications, consider this 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. Key revision takeaway: The class-over-interface requirement is not arbitrary; it is a direct consequence of how TypeScript decorators work.