Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сервисы и внедрение зависимостей

Сервис — класс, в котором живёт логика приложения: хранение, поиск и изменение данных. Контроллер только принимает запрос и вызывает сервис. 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 своего модуля, а если он из другого модуля — что тот модуль его экспортирует.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.