Впровадження служби Nest.js з іншого модуля

Інтеграція сервісу з іншого модуля в Nest.js передбачає кілька етапів, які гарантують правильне впровадження залежностей та організовану структуру модулів. Розглянемо процес експорту та імпорту сервісів на прикладі двох окремих модулів.

Створення проєкту Nest.js

Для початку роботи з Nest.js необхідно мати встановлений CLI на вашому пристрої. Якщо його немає, скористайтеся наступною командою для інсталяції:

 npm install -g @nestjs/cli

Після успішної інсталяції Nest.js CLI, запустіть наступну команду для генерації нового проєкту:

 nest new <назва-проєкту>

Замініть “<назва-проєкту>” на бажану назву вашого проєкту. Ця команда створить новий проєкт Nest.js з вказаним ім’ям.

Наразі структура вашого проєкту має бути схожою на зображення нижче:

Для практики інтеграції сервісу з одного модуля до іншого, ми створимо два модулі: module-a та module-b. Для кожного з них також згенеруємо відповідні файли сервісу та контролера.

Запустіть цю команду для генерації модуля a:

 nest generate module module-a

І аналогічну команду для module-b:

 nest generate module module-b

Далі, створіть файли сервісу та контролера для module-a за допомогою цієї команди:

 nest generate service module-a && nest generate controller module-a

І виконайте аналогічну команду для module-b:

 nest generate service module-b && nest generate controller module-b

Тепер структура вашого проєкту повинна включати каталоги src/module-a та src/module-b, як показано на зображенні:

Експорт сервісу з модуля A

Для експорту сервісу з модуля `module-a`, потрібно оголосити його як експорт у файлі модуля `module-a` (`module-a.module.ts`). За замовчуванням, Nest.js CLI не додає масив експорту у декоратор `@Module`, тому створений файл модуля буде виглядати наступним чином:

import { Module } from '@nestjs/common';
import { ModuleAService } from './module-a.service';
import { ModuleAController } from './module-a.controller';

@Module({
  providers: [ModuleAService],
  controllers: [ModuleAController],
})
export class ModuleAModule {}

Щоб зробити сервіс `service-a` (`module-a.service.ts`) доступним для модулів, що імпортують `module-a`, створіть масив `exports` у декораторі `@Module` та додайте до нього `ModuleAService`.

Ось приклад:

import { Module } from '@nestjs/common';
import { ModuleAService } from './module-a.service';
import { ModuleAController } from './module-a.controller';

@Module({
  providers: [ModuleAService],
  controllers: [ModuleAController],
  exports: [ModuleAService],
})
export class ModuleAModule {}

Для тестування, додайте просту функцію до файлу сервісу `module-a` (`module-a.service.ts`):

import { Injectable } from '@nestjs/common';

@Injectable()
export class ModuleAService {
  getHello(): string {
    return 'Hello from Module A!';
  }
}

Ця функція повертає звичайний текстовий рядок. Для перевірки правильності імпорту цього сервісу, ми викличемо цю функцію з `module-b` після впровадження `service-a`.

Імпорт сервісу в модуль B

Для імпорту одного модуля в інший, необхідно вказати його у масиві `imports` модуля-отримувача. У цьому випадку, потрібно додати `module-a` до масиву `imports` декоратора `@Module` в `module-b`.

Як і раніше, Nest.js CLI не створює масив `imports` автоматично, тому його потрібно додати вручну.

Спочатку імпортуйте батьківський модуль (`module-a.module.ts`) у модуль-отримувач (`module-b.module.ts`), створіть масив `imports` та додайте `ModuleAModule` до нього:

import { Module } from '@nestjs/common';
import { ModuleBController } from './module-b.controller';
import { ModuleBService } from './module-b.service';
import { ModuleAModule } from '../module-a/module-a.module';

@Module({
  imports: [ModuleAModule],
  controllers: [ModuleBController],
  providers: [ModuleBService],
})
export class ModuleBModule {}

Далі, відкрийте файл `module-b.service.ts` та імпортуйте декоратор `Inject` і `ModuleAServerice` з `@nestjs/common` та `../module-a/module-a.service` відповідно:

import { Injectable, Inject } from '@nestjs/common';
import { ModuleAService } from '../module-a/module-a.service';

Декоратор `Inject` позначає свій параметр як ціль для впровадження залежностей.

Далі, додайте наступний код до класу `ModuleBService`:

@Inject(ModuleAService)
private readonly moduleAService: ModuleAService;

Цей блок коду надає вашому `ModuleBService` доступ до методів, доступних у вашому `ModuleAService`.

Тепер ви можете протестувати сервіс, викликавши метод `getHello` модуля `ModuleAService`.

import { Injectable, Inject } from '@nestjs/common';
import { ModuleAService } from 'src/module-a/module-a.service';

@Injectable()
export class ModuleBService {
  @Inject(ModuleAService)
  private readonly moduleAService: ModuleAService;

  getHello(): string {
    return this.moduleAService.getHello();
  }
}

Далі, відкрийте файл `module-b.controller.ts` та замініть згенерований код наступним:

import { Controller, Get } from '@nestjs/common';
import { ModuleBService } from './module-b.service';

@Controller('module-b')
export class ModuleBController {
  constructor(private readonly moduleBService: ModuleBService) {}

  @Get('/hello')
  getHello(): string {
    return this.moduleBService.getHello();
  }
}

Наведений вище блок коду встановлює обробник маршруту GET для функції `getHello`.

Нарешті, виконайте GET-запит за допомогою `curl` до `localhost:3000/module-b/hello`. Команда має вивести “Hello from Module A!” до вашої консолі.

Ви успішно впровадили сервіс в інший модуль. Це може стати корисним, коли ви розробляєте API за допомогою Nest.js, де кілька модулів повинні викликати методи один одного.

Переваги міжмодульного впровадження

Хоча прямий виклик сервісу з іншого модуля може здаватися спочатку простішим, у довгостроковій перспективі це може призвести до більш складної, менш зручної для обслуговування та менш масштабованої системи.

З іншого боку, міжмодульне впровадження сприяє модульності коду та повторному використанню, що полегшує його обслуговування. Крім того, воно централізує залежності, покращує тестування та підтримує масштабовану, декомпоновану архітектуру.