Пошук уроків, статей та іншого контенту
Створите глобальний модуль і визначите залежності, доступні в усьому застосунку.
Звичайний модуль NestJS доступний лише в тих модулях, які явно імпортують його через imports.
Глобальний модуль відрізняється тим, що його експортовані провайдери стають доступними в усьому застосунку. Іншим модулям не потрібно щоразу додавати цей модуль до imports.
Глобальний модуль позначається декоратором @Global():
import { Global, Module } from '@nestjs/common';
@Global()
@Module({
providers: [],
exports: [],
})
export class SharedModule {}Глобальність поширюється не на всі провайдери модуля, а лише на ті, які додані до його exports.
Розглянемо модуль для роботи з підключенням до бази даних. Сервіс підключення може знадобитися в багатьох частинах застосунку, тому його зручно зробити глобальним.
import { Injectable } from '@nestjs/common';
@Injectable()
export class DatabaseService {
private connected = false;
connect(): void {
this.connected = true;
console.log('Підключення до бази даних встановлено');
}
isConnected(): boolean {
return this.connected;
}
}import { Global, Module } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Global()
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}У цьому прикладі:
DatabaseService зареєстрований як провайдер;
DatabaseService доданий до exports;
@Global() робить експортований провайдер доступним для всіх модулів застосунку.
Глобальний модуль потрібно імпортувати щонайменше один раз. Зазвичай це роблять у кореневому AppModule.
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { AppController } from './app.controller';
@Module({
imports: [DatabaseModule],
controllers: [AppController],
})
export class AppModule {}Імпорт у AppModule реєструє глобальний модуль у контексті застосунку. Після цього DatabaseService можна використовувати в інших модулях без повторного імпорту DatabaseModule.
Створимо контролер, який використовує DatabaseService.
import { Controller, Get } from '@nestjs/common';
import { DatabaseService } from './database/database.service';
@Controller()
export class AppController {
constructor(private readonly databaseService: DatabaseService) {}
@Get('database-status')
getDatabaseStatus(): string {
return this.databaseService.isConnected()
? 'База даних підключена'
: 'База даних не підключена';
}
}AppController не імпортує DatabaseModule у своєму модулі. NestJS знаходить DatabaseService серед експортованих провайдерів глобального модуля та передає його в конструктор.
Повний мінімальний приклад:
// database/database.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class DatabaseService {
private connected = false;
connect(): void {
this.connected = true;
}
isConnected(): boolean {
return this.connected;
}
}// database/database.module.ts
import { Global, Module } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Global()
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { DatabaseService } from './database/database.service';
@Controller()
export class AppController {
constructor(private readonly databaseService: DatabaseService) {
this.databaseService.connect();
}
@Get('database-status')
getDatabaseStatus(): string {
return this.databaseService.isConnected()
? 'База даних підключена'
: 'База даних не підключена';
}
}// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { DatabaseModule } from './database/database.module';
@Module({
imports: [DatabaseModule],
controllers: [AppController],
})
export class AppModule {}// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();Після запуску застосунку запит до GET /database-status поверне:
База даних підключенаexports у глобальному модуліДекоратор @Global() сам по собі не робить усі провайдери доступними в застосунку.
Наприклад, у цьому модулі DatabaseService не буде доступним за його межами:
import { Global, Module } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Global()
@Module({
providers: [DatabaseService],
})
export class DatabaseModule {}Причина — сервіс не доданий до exports.
Правильний варіант:
import { Global, Module } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Global()
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}Можна експортувати кілька провайдерів:
@Global()
@Module({
providers: [DatabaseService, CacheService, LoggerService],
exports: [DatabaseService, CacheService, LoggerService],
})
export class InfrastructureModule {}Звичайний модуль потрібно імпортувати в кожен модуль, який використовує його провайдер:
@Module({
imports: [DatabaseModule],
})
export class UsersModule {}@Module({
imports: [DatabaseModule],
})
export class OrdersModule {}Для глобального модуля достатньо одноразового імпорту в кореневому модулі:
@Global()
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}@Module({
imports: [DatabaseModule],
})
export class AppModule {}Після цього DatabaseService можна впроваджувати і в UsersModule, і в OrdersModule без додавання DatabaseModule до їхніх imports.
Глобальні модулі підходять для залежностей, які мають загальне призначення для всього застосунку:
конфігурація застосунку;
логер;
підключення до бази даних;
спільний кеш;
інфраструктурні сервіси.
Глобальність варто використовувати обмежено. Якщо зробити глобальними багато модулів, залежності стануть менш очевидними: за кодом модуля буде складніше зрозуміти, звідки надходять його сервіси.
Для функціональних залежностей краще залишати явні імпорти:
@Module({
imports: [PaymentsModule],
providers: [OrdersService],
})
export class OrdersModule {}Так структура застосунку залишається зрозумілою, а залежності модуля видно без пошуку по всьому проєкту.
AppModuleНавіть глобальний модуль потрібно зареєструвати в одному з модулів застосунку, зазвичай у кореневому:
@Module({
imports: [DatabaseModule],
})
export class AppModule {}Без цього NestJS не створить модуль і не зареєструє його провайдери.
Провайдер має бути одночасно в providers і exports:
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}Наявність провайдера лише в providers робить його доступним тільки всередині цього модуля.
Глобальними стають лише експортовані провайдери. Імпортований у глобальний модуль звичайний модуль не обов’язково робить усі свої провайдери доступними в усьому застосунку.
@Global()Не слід робити глобальним кожен модуль. Глобальність виправдана для справді спільних сервісів, а не для всіх функцій застосунку.
Глобальний модуль створюється за допомогою декоратора @Global().
Глобальний модуль потрібно імпортувати щонайменше один раз, зазвичай у AppModule.
Провайдер має бути доданий до exports, щоб стати доступним іншим модулям.
Експортовані провайдери глобального модуля можна впроваджувати без повторного імпорту модуля.
Глобальні модулі зручні для спільних інфраструктурних залежностей.
Надмірне використання глобальних модулів ускладнює розуміння залежностей застосунку.