Injectarea unui serviciu Nest.js dintr-un modul diferit

În cadrul Nest.js, procesul de injectare a unui serviciu dintr-un modul în altul necesită o serie de etape menite să asigure o gestionare corespunzătoare a dependențelor și o structură clară a modulelor. Vom explora mecanismele de export și import de servicii folosind două module ca exemplu concret, oferind o înțelegere detaliată a acestui concept fundamental.

Crearea unui proiect Nest.js

Pentru a iniția un nou proiect Nest.js, este necesar să aveți instalat în prealabil CLI-ul. În cazul în care acesta lipsește, puteți realiza instalarea folosind următoarea comandă:

 npm install -g @nestjs/cli

Odată instalat CLI-ul, puteți utiliza comanda de mai jos pentru a genera un nou proiect, specificând un nume personalizat:

 nest new <nume-proiect>

Înlocuiți „<nume-proiect>” cu numele dorit. Executarea acestei comenzi va iniția generarea unui nou proiect Nest.js cu configurația specificată.

Structura inițială a proiectului va fi similară cu cea prezentată în imaginea următoare:

Pentru a demonstra injectarea unui serviciu dintr-un modul în altul, vom crea două module, denumite modulul-a și modulul-b. În plus, vom genera și fișierele aferente serviciilor și controlerelor pentru fiecare modul.

Pentru a genera modulul-a, utilizați următoarea comandă:

 nest generate module module-a

Similar, pentru a genera modulul-b, folosiți comanda echivalentă:

 nest generate module module-b

Generați apoi fișierele de serviciu și controler pentru modulul-a, executând următoarea comandă:

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

Repetați procesul pentru modulul-b:

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

În acest moment, structura proiectului ar trebui să includă directoarele src/module-a și src/module-b, ca în imaginea de mai jos:

Exportarea unui serviciu din modulul A

Pentru a permite accesul la serviciul modulului-a din exterior, acesta trebuie inclus în lista de exporturi a fișierului modulului-a (module-a.module.ts). Implicit, CLI-ul Nest.js nu generează o matrice pentru exporturi în decoratorul @Module, deci fișierul generat va fi similar cu următorul:

 
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 {}

Pentru a face serviciul (module-a.service.ts) accesibil modulelor care importă modulul-a, vom adăuga o matrice de exporturi în cadrul decoratorului @Module și vom include ModuleAService în aceasta.

Codul modificat va arăta astfel:

 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 {}

În scopuri demonstrative, adăugați o funcție simplă la fișierul de serviciu al modulului-a (module-a.service.ts):

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

@Injectable()
export class ModuleAService {
  getHello(): string {
    return 'Salut din Modulul A!';
  }
}

Această funcție va returna un șir simplu. Pentru a verifica importul corect al serviciului, vom apela această funcție din modulul-b, după injectarea serviciului-a.

Importarea unui serviciu în modulul B

Pentru a utiliza un modul într-un altul, acesta trebuie inclus în lista de importuri a modulului destinatar. În acest caz, modulul-a trebuie adăugat în matricea de importuri a decoratorului @Module din modulul-b.

Similar ca mai devreme, CLI-ul Nest.js nu generează automat o matrice de importuri, deci trebuie să o creăm manual.

Începem prin a importa modulul părinte (module-a.module.ts) în modulul destinatar (module-b.module.ts), adăugăm matricea de importuri și includem ModuleAModule în aceasta:

 
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 {}

Apoi, deschidem fișierul module-b.service.ts și importăm decoratorul Inject și ModuleAService din @nests/common și, respectiv, ../module-a/module-a.service:

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

Decoratorul Inject marchează parametrul ca țintă pentru injectarea dependenței.

În clasa ModuleBService, adăugăm următorul cod:

 @Inject(ModuleAService)
  private readonly moduleAService: ModuleAService;

Acest cod oferă clasei ModuleBService acces la metodele disponibile în ModuleAService.

Putem testa serviciul apelând metoda getHello a 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();
  }
}

Acum, deschidem fișierul module-b.controller.ts și înlocuim codul generat cu următorul bloc:

 
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();
  }
}

Acest cod configurează un handler de rută GET pentru funcția getHello.

În final, realizăm o solicitare GET cu curl către localhost:3000/module-b/hello. Consola ar trebui să afișeze mesajul „Salut din Modulul A!”.

Am reușit să injectăm un serviciu într-un alt modul. Acest lucru este util în dezvoltarea API-urilor cu Nest.js, care implică multiple module ce necesită apelarea metodelor între ele.

Beneficiile injectării între module

Deși apelarea directă a unui serviciu dintr-un alt modul poate părea o soluție mai simplă la început, pe termen lung, aceasta poate duce la un sistem mai complex, mai dificil de întreținut și mai puțin scalabil.

Injectarea între module, pe de altă parte, promovează modularitatea și reutilizarea codului, facilitând întreținerea. În plus, centralizează dependențele, îmbunătățește testabilitatea și susține o arhitectură scalabilă și decuplată.