DTO: описание входных данных
DTO (Data Transfer Object, «объект для передачи данных») — класс, который описывает, какие поля клиент присылает в запросе. Вместо безымянного типа прямо в методе контроллера у данных появляется имя и отдельный файл, а позже к полям добавятся правила проверки.
Где лежат DTO
Команда nest g resource posts уже создала папку src/posts/dto/ с двумя файлами. Если ресурс создавался по частям, создайте файл src/posts/dto/create-post.dto.ts вручную:
// src/posts/dto/create-post.dto.ts
export class CreatePostDto {
title: string;
content: string;
}Класс перечисляет поля, которые нужны для создания статьи. Методов у него нет — это просто описание формы данных.
DTO в контроллере
Тип аргумента с @Body() теперь — этот класс:
// 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: 'Статья создана', title: createPostDto.title };
}
}Отправьте тот же POST-запрос, что и в прошлом уроке, — ответ не изменится. Зато редактор теперь подсказывает поля title и content, а сигнатура метода сама объясняет, что ждёт маршрут.
Почему класс, а не интерфейс
Интерфейсы и типы TypeScript исчезают при компиляции в JavaScript. Классы остаются, и NestJS во время работы видит, к какому классу относится аргумент. На этом построена проверка данных: в уроке о валидации к полям DTO добавятся декораторы вроде @IsString(), и NestJS будет отклонять неправильные запросы. С интерфейсом это невозможно.
DTO для изменения
При изменении статьи клиент может прислать только часть полей. Писать второй класс с теми же полями, но необязательными, не нужно — его создаёт функция PartialType():
// 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 те же поля title и content, но оба необязательные. Пакет @nestjs/mapped-types ставится вместе с ресурсом; если его нет, выполните npm i @nestjs/mapped-types.
// src/posts/posts.controller.ts
@Patch(':id')
update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) {
return { id, changes: updatePostDto };
}Запрос PATCH /posts/5 с телом {"title":"Новый заголовок"} вернёт номер статьи и присланные изменения.
Имена файлов
| Файл | Класс | Для чего |
|---|---|---|
create-post.dto.ts | CreatePostDto | Тело POST /posts |
update-post.dto.ts | UpdatePostDto | Тело PATCH /posts/:id |
create-comment.dto.ts | CreateCommentDto | Тело POST /posts/:id/comments |
Частая ошибка
Тип в TypeScript — только подсказка для редактора. Пока не подключена валидация, @Body() dto: CreatePostDto пропустит любой JSON: без поля title, с числом вместо строки, с лишними полями. Не полагайтесь на DTO как на защиту, пока не прошли урок о ValidationPipe.