Сервисы и внедрение зависимостей
Сервис — класс, в котором живёт логика приложения: хранение, поиск и изменение данных. Контроллер только принимает запрос и вызывает сервис. NestJS называет такие классы провайдерами: он сам создаёт их объекты и передаёт тем, кому они нужны. Этот механизм называется внедрением зависимостей (dependency injection, DI).
Класс статьи
Сначала опишите, как выглядит статья. Файл src/posts/entities/post.entity.ts создала команда nest g resource:
// src/posts/entities/post.entity.ts
export class Post {
id: number;
title: string;
content: string;
}Позже этот класс станет таблицей базы данных, а пока статьи будут храниться в массиве.
Сервис
nest g service posts// src/posts/posts.service.ts
import { Injectable } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { Post } from './entities/post.entity';
@Injectable()
export class PostsService {
private posts: Post[] = [];
private nextId = 1;
findAll(): Post[] {
return this.posts;
}
findOne(id: number): Post | undefined {
return this.posts.find((post) => post.id === id);
}
create(dto: CreatePostDto): Post {
const post = { id: this.nextId++, ...dto };
this.posts.push(post);
return post;
}
}Декоратор @Injectable() отмечает класс как провайдер, которым NestJS может управлять. CLI уже добавил PostsService в providers модуля PostsModule.
Внедрение в контроллер
Контроллер не создаёт сервис через new, а просит его в конструкторе:
// src/posts/posts.controller.ts
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { PostsService } from './posts.service';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll() {
return this.postsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.postsService.findOne(Number(id));
}
@Post()
create(@Body() dto: CreatePostDto) {
return this.postsService.create(dto);
}
}Запись private readonly postsService в конструкторе — сокращение TypeScript: она объявляет свойство и сразу присваивает ему аргумент. NestJS смотрит на тип PostsService, находит этот провайдер в модуле и передаёт его объект.
Создайте статью POST-запросом, а затем откройте http://localhost:3000/posts — она будет в списке. После перезапуска сервера массив очищается: база данных появится в четвёртой главе.
Один объект на всё приложение
NestJS создаёт сервис один раз и раздаёт один и тот же объект всем, кто его попросит. Так CommentsService из урока о модулях получает те же статьи, что и контроллер:
// src/comments/comments.service.ts
import { Injectable } from '@nestjs/common';
import { PostsService } from '../posts/posts.service';
@Injectable()
export class CommentsService {
constructor(private readonly postsService: PostsService) {}
canComment(postId: number): boolean {
return this.postsService.findOne(postId) !== undefined;
}
}Частая ошибка
Если сервис не записан в providers модуля, NestJS при запуске выдаст Nest can't resolve dependencies of the PostsController (?). Знак вопроса стоит на месте аргумента, который не удалось найти. Проверьте, что сервис помечен декоратором и указан в providers своего модуля, а если он из другого модуля — что тот модуль его экспортирует.