Skip to main content
Практика завдань

Що таке динамічні модулі в NestJS і коли їх використовувати?

Динамічні модулі в NestJS

Динамічні модулі дозволяють створювати конфігуровані, повторно використовувані модулі, які приймають параметри під час імпорту. Саме так працюють бібліотеки, такі як @nestjs/jwt, @nestjs/bull та TypeOrmModule.


Статичні та динамічні модулі

Статичний модуль

typescript
@Module({ providers: [CatsService], exports: [CatsService], }) export class CatsModule {} // Використання — без конфігурації @Module({ imports: [CatsModule], }) export class AppModule {}

Динамічний модуль

typescript
// Використання — з конфігурацією @Module({ imports: [ DatabaseModule.forRoot({ host: 'localhost', port: 5432 }), ], }) export class AppModule {}

Створення динамічного модуля

Основний шаблон: forRoot / forFeature

typescript
export interface DatabaseModuleOptions { host: string; port: number; username: string; password: string; database: string; } @Module({}) export class DatabaseModule { static forRoot(options: DatabaseModuleOptions): DynamicModule { return { module: DatabaseModule, global: true, // Доступний скрізь providers: [ { provide: 'DATABASE_OPTIONS', useValue: options, }, { provide: 'DATABASE_CONNECTION', useFactory: async (opts: DatabaseModuleOptions) => { return createConnection(opts); }, inject: ['DATABASE_OPTIONS'], }, DatabaseService, ], exports: ['DATABASE_CONNECTION', DatabaseService], }; } }

Асинхронна конфігурація (forRootAsync)

Для випадків, коли параметри надходять з середовища або інших сервісів:

typescript
@Module({}) export class DatabaseModule { static forRoot(options: DatabaseModuleOptions): DynamicModule { return { module: DatabaseModule, providers: [ { provide: 'DATABASE_OPTIONS', useValue: options }, DatabaseService, ], exports: [DatabaseService], }; } static forRootAsync(options: { imports?: any[]; useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions; inject?: any[]; }): DynamicModule { return { module: DatabaseModule, imports: options.imports || [], providers: [ { provide: 'DATABASE_OPTIONS', useFactory: options.useFactory, inject: options.inject || [], }, DatabaseService, ], exports: [DatabaseService], }; } }

Використання:

typescript
@Module({ imports: [ DatabaseModule.forRootAsync({ imports: [ConfigModule], useFactory: (config: ConfigService) => ({ host: config.get('DB_HOST'), port: config.get('DB_PORT'), username: config.get('DB_USER'), password: config.get('DB_PASS'), database: config.get('DB_NAME'), }), inject: [ConfigService], }), ], }) export class AppModule {}

Шаблон forFeature

Для конфігурації на рівні модуля (наприклад, сутності TypeORM для кожного модуля):

typescript
@Module({}) export class StorageModule { static forRoot(options: StorageOptions): DynamicModule { return { module: StorageModule, global: true, providers: [ { provide: 'STORAGE_OPTIONS', useValue: options }, StorageService, ], exports: [StorageService], }; } static forFeature(bucket: string): DynamicModule { return { module: StorageModule, providers: [ { provide: 'STORAGE_BUCKET', useValue: bucket }, { provide: `STORAGE_${bucket.toUpperCase()}`, useFactory: (storageService: StorageService) => { return storageService.getBucket(bucket); }, inject: [StorageService], }, ], exports: [`STORAGE_${bucket.toUpperCase()}`], }; } }
typescript
// У функціональних модулях @Module({ imports: [StorageModule.forFeature('avatars')], }) export class UsersModule {} @Module({ imports: [StorageModule.forFeature('documents')], }) export class DocumentsModule {}

Приклад з реального світу: Модуль електронної пошти

typescript
@Module({}) export class EmailModule { static forRoot(options: EmailModuleOptions): DynamicModule { return { module: EmailModule, global: true, providers: [ { provide: 'EMAIL_OPTIONS', useValue: options }, EmailService, ], exports: [EmailService], }; } static forRootAsync(options: EmailAsyncOptions): DynamicModule { return { module: EmailModule, global: true, imports: options.imports || [], providers: [ { provide: 'EMAIL_OPTIONS', useFactory: options.useFactory, inject: options.inject || [], }, EmailService, ], exports: [EmailService], }; } }

Підсумок поширених шаблонів

МетодСценарій використання
forRoot()Глобальна конфігурація (один раз у AppModule)
forRootAsync()Асинхронна конфігурація (з ConfigService тощо)
forFeature()Конфігурація на рівні модуля
register()Те ж саме, що і forRoot, але може бути викликано кілька разів
registerAsync()Асинхронна версія register

Ключове спостереження: Динамічні модулі — це система плагінів NestJS. Використовуйте forRoot для одноразової глобальної налаштування, forFeature для налаштування на рівні модуля. Асинхронні варіанти дозволяють використовувати впровадження залежностей для отримання значень конфігурації.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань