DTOs: describing input data
A DTO (Data Transfer Object) is a class that describes which fields the client sends in a request. Instead of an anonymous type right in the controller method, the data gets a name and its own file, and later validation rules are added to the fields.
Where DTOs live
nest g resource posts has already created the src/posts/dto/ folder with two files. If you built the resource piece by piece, create src/posts/dto/create-post.dto.ts by hand:
// src/posts/dto/create-post.dto.ts
export class CreatePostDto {
title: string;
content: string;
}The class lists the fields needed to create a post. It has no methods — it only describes the shape of the data.
A DTO in the controller
The type of the @Body() argument is now this class:
// src/posts/posts.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
@Controller('posts')
export class PostsController {
@Post()
create(@Body() createPostDto: CreatePostDto) {
return { message: 'Post created', title: createPostDto.title };
}
}Send the same POST request as in the previous lesson — the response is the same. But the editor now suggests the title and content fields, and the method signature itself explains what the route expects.
Why a class and not an interface
TypeScript interfaces and types disappear when compiled to JavaScript. Classes remain, and NestJS can see at runtime which class an argument belongs to. Data validation is built on this: in the validation lesson decorators like @IsString() are added to DTO fields, and NestJS starts rejecting bad requests. That is impossible with an interface.
A DTO for updates
When updating a post, the client may send only some of the fields. You do not need a second class with the same fields made optional — PartialType() creates it:
// src/posts/dto/update-post.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class UpdatePostDto extends PartialType(CreatePostDto) {}UpdatePostDto has the same title and content fields, but both are optional. The @nestjs/mapped-types package is installed together with a resource; if it is missing, run npm i @nestjs/mapped-types.
// src/posts/posts.controller.ts
@Patch(':id')
update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) {
return { id, changes: updatePostDto };
}A PATCH /posts/5 request with the body {"title":"New title"} returns the post number and the submitted changes.
File names
| File | Class | Used for |
|---|---|---|
create-post.dto.ts | CreatePostDto | Body of POST /posts |
update-post.dto.ts | UpdatePostDto | Body of PATCH /posts/:id |
create-comment.dto.ts | CreateCommentDto | Body of POST /posts/:id/comments |
Common mistake
A TypeScript type is only a hint for the editor. Until validation is enabled, @Body() dto: CreatePostDto lets any JSON through: without title, with a number instead of a string, with extra fields. Do not rely on a DTO as protection until you have done the ValidationPipe lesson.