Dinamik modullar
Modullar bobi Nest modullarining asoslarini qamrab oladi va dinamik modullar haqida qisqacha kirish beradi. Ushbu bob dinamik modullar mavzusini kengaytiradi. Yakunda siz ularning
Modullar bobi Nest modullarining asoslarini qamrab oladi va dinamik modullar haqida qisqacha kirish beradi. Ushbu bob dinamik modullar mavzusini kengaytiradi. Yakunda siz ularning nimaligini va qachon hamda qanday ishlatishni yaxshi tushunib olasiz.
Kirish
Hujjatlarning Overview bo'limidagi ilova kodi misollarining aksariyati odatiy, ya'ni statik modullardan foydalanadi. Modullar providers va controllers kabi komponentlar guruhlarini belgilaydi; ular ilovaning modulli qismi sifatida birga ishlaydi. Modullar ushbu komponentlar uchun execution context yoki scope taqdim etadi. Masalan, modul ichida aniqlangan provayderlar ularni eksport qilish shartisiz modulning boshqa a'zolariga ko'rinadi. Provayder moduldan tashqarida ko'rinishi kerak bo'lsa, avval u o'zining host modulidan eksport qilinadi, so'ng consuming modulga import qilinadi.
Keling, tanish misolni ko'rib chiqamiz.
Avval UsersService ni taqdim etish va eksport qilish uchun UsersModule ni aniqlaymiz. UsersModule UsersService uchun host modul hisoblanadi.
1import { Module } from '@nestjs/common';
2import { UsersService } from './users.service';
3
4@Module({
5 providers: [UsersService],
6 exports: [UsersService],
7})
8export class UsersModule {}Keyin UsersModule ni import qiladigan AuthModule ni aniqlaymiz; shu bilan UsersModule eksport qilgan provayderlar AuthModule ichida mavjud bo'ladi:
1import { Module } from '@nestjs/common';
2import { AuthService } from './auth.service';
3import { UsersModule } from '../users/users.module';
4
5@Module({
6 imports: [UsersModule],
7 providers: [AuthService],
8 exports: [AuthService],
9})
10export class AuthModule {}Bu konstruktsiyalar UsersService ni, masalan, AuthModule ichida joylashgan AuthService ga in'eksiya qilishga imkon beradi:
1import { Injectable } from '@nestjs/common';
2import { UsersService } from '../users/users.service';
3
4@Injectable()
5export class AuthService {
6 constructor(private usersService: UsersService) {}
7 /*
8 Implementation that makes use of this.usersService
9 */
10}Buni statik modul bog'lash deb ataymiz. Nest modullarni bir-biriga ulash uchun kerakli barcha ma'lumotlar host va consuming modullarda allaqachon e'lon qilingan. Endi bu jarayonda nimalar sodir bo'lishini ochib beramiz. Nest UsersService ni AuthModule ichida quyidagicha mavjud qiladi:
UsersModuleni instansiyalaydi, bundaUsersModulening o'zi iste'mol qiladigan boshqa modullarni tranzitiv tarzda import qiladi va har qanday bog'liqliklarni tranzitiv yechadi (qarang Custom providers).AuthModuleni instansiyalaydi vaUsersModuleeksport qilgan provayderlarniAuthModulekomponentlariga mavjud qiladi (xuddi ularAuthModuleda e'lon qilingandek).UsersServiceinstansiyasiniAuthServicega in'eksiya qiladi.
Dinamik modul use-case
Statik modul bog'lashda consuming modul host modul provayderlari qanday sozlanishiga ta'sir ko'rsata olmaydi. Bu nega muhim? Turli use-case'larda turlicha ishlashi kerak bo'lgan umumiy modul holatini ko'rib chiqing. Bu ko'plab tizimlardagi "plugin" tushunchasiga o'xshaydi, ya'ni umumiy imkoniyat consumer tomonidan ishlatilishidan oldin qandaydir konfiguratsiyani talab qiladi.
Nestdagi yaxshi misol - konfiguratsiya moduli. Ko'plab ilovalar konfiguratsiya tafsilotlarini tashqi qilish uchun konfiguratsiya modulidan foydalanishni foydali deb biladi. Bu turli deploymentlarda ilova sozlamalarini dinamik o'zgartirishni osonlashtiradi: masalan, developerlar uchun development bazasi, staging/testing muhiti uchun staging bazasi va hokazo. Konfiguratsiya parametrlarini boshqarishni konfiguratsiya moduliga delegatsiya qilish orqali ilova manba kodi konfiguratsiya parametrlaridan mustaqil qoladi.
Muammo shundaki, konfiguratsiya modulining o'zi umumiy ("plugin"ga o'xshash) bo'lgani sababli, u consuming modul tomonidan moslashtirilishi kerak. Bu yerda dinamik modullar ishga tushadi. Dinamik modul imkoniyatlaridan foydalanib, konfiguratsiya modulimizni dinamik qilamiz, shunda consuming modul import vaqtida konfiguratsiya moduli qanday moslashtirilishini API orqali boshqara oladi.
Boshqacha qilib aytganda, dinamik modullar bir modulni boshqasiga import qilish va import vaqtida u modulning xossalari hamda xatti-harakatini moslashtirish uchun API taqdim etadi; bu hozirgacha ko'rgan statik bog'lashdan farq qiladi.
Config modul misoli
Bu bo'lim uchun configuration chapter dagi kod misolining bazaviy versiyasidan foydalanamiz. Ushbu bob oxiridagi yakuniy versiya ishlaydigan misol sifatida bu yerda mavjud.
Talabimiz - ConfigModule options obyektini qabul qilib, o'zini moslashtira olsin. Biz qo'llab-quvvatlamoqchi bo'lgan imkoniyat mana shunday. Bazaviy namunada .env fayli joylashuvi project root katalogiga qattiq kodlangan. Faraz qilaylik, buni sozlanadigan qilishni xohlaymiz, ya'ni .env fayllarini xohlagan katalogingizda boshqara olasiz. Masalan, project root ostida config nomli katalogda (ya'ni src bilan yonma-yon) turli .env fayllarini saqlamoqchisiz. ConfigModule ni turli loyihalarda ishlatganda turli kataloglarni tanlash imkonini xohlaysiz.
Dinamik modullar import qilinayotgan modulga parametrlar uzatish imkonini beradi, shunda uning xatti-harakatini o'zgartirishimiz mumkin. Keling, bu qanday ishlashini ko'raylik. Avval consuming modul nuqtai nazaridan yakuniy ko'rinish qanday bo'lishini ko'z oldiga keltirish va keyin orqaga qarab yurish foydali. Avvalo, ConfigModule ni statik import qilish misolini tezkor ko'rib chiqaylik (ya'ni import qilingan modul xatti-harakatiga ta'sir qila olmaydigan yondashuv). @Module() dekoratoridagi imports massiviga diqqat qiling:
1import { Module } from '@nestjs/common';
2import { AppController } from './app.controller';
3import { AppService } from './app.service';
4import { ConfigModule } from './config/config.module';
5
6@Module({
7 imports: [ConfigModule],
8 controllers: [AppController],
9 providers: [AppService],
10})
11export class AppModule {}Endi konfiguratsiya obyektini uzatadigan dinamik modul importi qanday ko'rinishini tasavvur qilaylik. Bu ikki misolda imports massividagi farqqa e'tibor bering:
1import { Module } from '@nestjs/common';
2import { AppController } from './app.controller';
3import { AppService } from './app.service';
4import { ConfigModule } from './config/config.module';
5
6@Module({
7 imports: [ConfigModule.register({ folder: './config' })],
8 controllers: [AppController],
9 providers: [AppService],
10})
11export class AppModule {}Yuqoridagi dinamik misolda nimalar sodir bo'lyapti? Harakatlanuvchi qismlar nimalar?
ConfigModuleoddiy sinf, demak undaregister()nomli static metod bo'lishi kerakligini xulosa qilamiz. Uning static ekanini bilamiz, chunki uniConfigModulesinfida chaqiryapmiz, sinf instansiyasida emas. Eslatma: bu metod, yaqin orada yaratadiganimiz, istalgan nomga ega bo'lishi mumkin, ammo odatga ko'ra uniforRoot()yokiregister()deb atashimiz kerak.register()metodini biz o'zimiz aniqlaymiz, shuning uchun u xohlagan argumentlarni qabul qilishi mumkin. Bu holatda biz mos xossalarga ega soddaoptionsobyektini qabul qilamiz, bu odatiy hol.register()metodimodulega o'xshash nimanidir qaytarishi kerakligini xulosa qilamiz, chunki uning qaytgan qiymati tanishimportsro'yxatida ko'rinadi va u modullar ro'yxatini o'z ichiga oladi.
Aslida, register() metodi qaytaradigan narsa DynamicModule bo'ladi. Dinamik modul - bu run-time paytida yaratilgan modul bo'lib, statik modul bilan aynan bir xil xossalarga ega, faqat bitta qo'shimcha module xossasi bor. Keling, @Module() dekoratoriga uzatilgan modul opsiyalariga diqqat qilib, statik modul deklaratsiyasining namunaviy ko'rinishini tezkor ko'rib chiqamiz:
1@Module({
2 imports: [DogsModule],
3 controllers: [CatsController],
4 providers: [CatsService],
5 exports: [CatsService]
6})Dinamik modullar xuddi shu interfeysga ega bo'lgan obyektni, qo'shimcha module xossasi bilan birga qaytarishi kerak. module xossasi modul nomi sifatida xizmat qiladi va u modul sinfi nomiga teng bo'lishi kerak, quyidagi misolda ko'rsatilganidek.
Dinamik modul uchun modul opsiyalari obyektining barcha xossalari ixtiyoriy, modulebundan mustasno.
Statik register() metodiga kelsak, endi uning vazifasi DynamicModule interfeysiga ega obyektni qaytarish ekanini ko'ramiz. Biz uni chaqirganimizda, statik holatda modul sinf nomini ro'yxatga qo'shgandek, amalda imports ro'yxatiga modul taqdim etyapmiz. Boshqacha aytganda, dinamik modul API shunchaki modulni qaytaradi, faqat @Module dekoratorida xossalarni qotirib qo'yish o'rniga, ularni dasturiy tarzda belgilaymiz.
Rasm to'liq bo'lishi uchun yana bir-ikki detalni ko'rib chiqish kerak:
- Endi
@Module()dekoratoriningimportsxossasi nafaqat modul sinf nomini (masalan,imports: [UsersModule]), balki dinamik modul qaytaradigan funksiyani ham qabul qilishi mumkinligini ayta olamiz (masalan,imports: [ConfigModule.register(...)]). - Dinamik modulning o'zi ham boshqa modullarni import qilishi mumkin. Biz bu misolda buni qilmaymiz, ammo agar dinamik modul boshqa modullardagi provayderlarga bog'liq bo'lsa, ularni ixtiyoriy
importsxossasi orqali import qilishingiz mumkin. Bu yana statik modul uchun@Module()dekoratori orqali metadata e'lon qilish bilan aynan analog.
Ushbu tushunchani o'zlashtirgach, dinamik ConfigModule deklaratsiyasi qanday ko'rinishda bo'lishi kerakligini ko'rib chiqamiz. Keling, sinab ko'ramiz.
1import { DynamicModule, Module } from '@nestjs/common';
2import { ConfigService } from './config.service';
3
4@Module({})
5export class ConfigModule {
6 static register(): DynamicModule {
7 return {
8 module: ConfigModule,
9 providers: [ConfigService],
10 exports: [ConfigService],
11 };
12 }
13}Endi qismlar qanday bog'lanishini aniq ko'rish mumkin. ConfigModule.register(...) chaqiruvi DynamicModule obyektini qaytaradi; uning xossalari hozirgacha @Module() dekoratori orqali metadata sifatida berganlarimiz bilan mohiyatan bir xil.
DynamicModule ni @nestjs/common dan import qiling.
Hozircha dinamik modulimiz unchalik qiziqarli emas, chunki u hali sozlanadigan imkoniyatni kiritmadi, biz buni xohlaganimizni aytgan edik. Endi shu masalani hal qilaylik.
Modul konfiguratsiyasi
ConfigModule xatti-harakatini moslashtirish uchun aniq yechim - static register() metodiga options obyektini uzatish, yuqorida taxmin qilganimizdek. Keling, consuming modulning imports xossasiga yana bir bor qaraylik:
1import { Module } from '@nestjs/common';
2import { AppController } from './app.controller';
3import { AppService } from './app.service';
4import { ConfigModule } from './config/config.module';
5
6@Module({
7 imports: [ConfigModule.register({ folder: './config' })],
8 controllers: [AppController],
9 providers: [AppService],
10})
11export class AppModule {}Bu options obyektini dinamik modulimizga uzatish masalasini chiroyli hal qiladi. Endi ConfigModule ichida options obyektidan qanday foydalanamiz? Keling, bu haqda o'ylab ko'ramiz. Biz bilamizki, ConfigModule aslida boshqa provayderlar foydalanishi uchun in'eksiya qilinadigan servis - ConfigService - ni taqdim etadigan va eksport qiladigan host hisoblanadi. Aslida options obyektini o'qib, xatti-harakatini moslashtirishi kerak bo'lgan ConfigService ning o'zi. Hozircha register() metodidan ConfigService ga options ni qandaydir yo'l bilan uzatishni bilamiz, deb faraz qilaylik. Shu faraz bilan, servisga options obyektidagi xossalarga asoslanib xatti-harakatini moslashtirish uchun bir nechta o'zgartirish kiritishimiz mumkin. (Eslatma: hozircha, biz uni qanday uzatishni hali aniqlamaganimiz sababli, options ni shunchaki hard-code qilamiz. Buni birozdan keyin to'g'rilaymiz).
1import { Injectable } from '@nestjs/common';
2import * as fs from 'node:fs';
3import * as path from 'node:path';
4import * as dotenv from 'dotenv';
5import { EnvConfig } from './interfaces';
6
7@Injectable()
8export class ConfigService {
9 private readonly envConfig: EnvConfig;
10
11 constructor() {
12 const options = { folder: './config' };
13
14 const filePath = `${process.env.NODE_ENV || 'development'}.env`;
15 const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
16 this.envConfig = dotenv.parse(fs.readFileSync(envFile));
17 }
18
19 get(key: string): string {
20 return this.envConfig[key];
21 }
22}Endi ConfigService options da ko'rsatgan katalogimizdan .env faylini topishni biladi.
Endi qolgan vazifa - register() qadamidan options obyektini qanday qilib ConfigService ga in'eksiya qilish. Va albatta, buning uchun dependency injection dan foydalanamiz. Bu muhim nuqta, shuning uchun uni yaxshi tushunib oling. Bizning ConfigModule ConfigService ni taqdim etadi. ConfigService esa faqat run-time paytida beriladigan options obyektiga bog'liq. Demak, run-time paytida avval options obyektini Nest IoC konteyneriga bog'lashimiz, keyin Nest uni ConfigService ga in'eksiya qilishi kerak. Custom providers bobidan esda bo'lsin, provayderlar istalgan qiymatni taqdim etishi mumkin, nafaqat servislarni, shuning uchun oddiy options obyektini dependency injection bilan boshqarish mumkin.
Avval options obyektini IoC konteyneriga bog'lashni hal qilamiz. Buni static register() metodimizda bajaramiz. Yodingizda bo'lsin, biz modulni dinamik tarzda tuzyapmiz va modul xossalaridan biri - provayderlar ro'yxati. Shuning uchun options obyektini provayder sifatida belgilashimiz kerak. Bu uni ConfigService ga in'eksiya qilinadigan qiladi; keyingi qadamda bundan foydalanamiz. Quyidagi kodda providers massiviga diqqat qiling:
1import { DynamicModule, Module } from '@nestjs/common';
2import { ConfigService } from './config.service';
3
4@Module({})
5export class ConfigModule {
6 static register(options: Record<string, any>): DynamicModule {
7 return {
8 module: ConfigModule,
9 providers: [
10 {
11 provide: 'CONFIG_OPTIONS',
12 useValue: options,
13 },
14 ConfigService,
15 ],
16 exports: [ConfigService],
17 };
18 }
19}Endi jarayonni 'CONFIG_OPTIONS' provayderini ConfigService ga in'eksiya qilish orqali yakunlaymiz. Sifat token bilan provayder aniqlaganimizda, @Inject() dekoratoridan foydalanishimiz kerakligi bu yerda ta'riflangan.
1import * as fs from 'node:fs';
2import * as path from 'node:path';
3import * as dotenv from 'dotenv';
4import { Injectable, Inject } from '@nestjs/common';
5import { EnvConfig } from './interfaces';
6
7@Injectable()
8export class ConfigService {
9 private readonly envConfig: EnvConfig;
10
11 constructor(@Inject('CONFIG_OPTIONS') private options: Record<string, any>) {
12 const filePath = `${process.env.NODE_ENV || 'development'}.env`;
13 const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
14 this.envConfig = dotenv.parse(fs.readFileSync(envFile));
15 }
16
17 get(key: string): string {
18 return this.envConfig[key];
19 }
20}Oxirgi eslatma: soddalik uchun yuqorida satrga asoslangan in'eksiya tokenidan ('CONFIG_OPTIONS') foydalandik, ammo eng yaxshi amaliyot - uni alohida faylda konstanta (yoki Symbol) sifatida aniqlash va o'sha faylni import qilish. Masalan:
1export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';Misol
Ushbu bobdagi kodning to'liq misolini bu yerda topishingiz mumkin.
Hamjamiyat ko'rsatmalari
@nestjs/ paketlarining ayrimlarida forRoot, register, va forFeature kabi metodlardan foydalanishni ko'rib, bu metodlar o'rtasidagi farq nima deb qiziqqan bo'lishingiz mumkin. Bu borada qat'iy qoidalar yo'q, ammo @nestjs/ paketlari quyidagi ko'rsatmalarga rioya qilishga harakat qiladi:
Modulni yaratishda:
-
register- dinamik modulni faqat chaqiruvchi modul uchun maxsus konfiguratsiya bilan sozlashni kutasiz. Masalan, Nestning@nestjs/axiosmoduli bilan:HttpModule.register({{ '{' }} baseUrl: 'someUrl' {{ '}' }}). Agar boshqa moduldaHttpModule.register({{ '{' }} baseUrl: 'somewhere else' {{ '}' }})ni ishlatsangiz, u boshqa konfiguratsiyaga ega bo'ladi. Buni xohlagancha ko'p modullar uchun qilishingiz mumkin. -
forRoot- dinamik modulni bir marta sozlab, shu konfiguratsiyani ko'p joyda qayta ishlatishni kutasiz (ko'pincha bu abstraksiyalangan bo'ladi). Shu sababli sizda bittaGraphQLModule.forRoot(), bittaTypeOrmModule.forRoot()va hokazo bo'ladi. -
forFeature- dinamik modulningforRootkonfiguratsiyasidan foydalanmoqchisiz, ammo chaqiruvchi modul ehtiyojlariga xos ba'zi konfiguratsiyani o'zgartirishingiz kerak (ya'ni bu modul qaysi repozitoriyaga kirishi kerakligi yoki logger qaysi kontekstdan foydalanishi kerakligi).
Ularning barchasida odatda async muqobillari ham bo'ladi: registerAsync, forRootAsync, va forFeatureAsync. Ma'nosi bir xil, faqat konfiguratsiya uchun Nestning Dependency Injection imkoniyatlaridan ham foydalaniladi.
Configurable module builder
registerAsync, forRootAsync kabi async metodlarni taqdim etadigan yuqori darajada sozlanadigan, dinamik modullarni qo'lda yaratish ancha murakkab, ayniqsa yangi boshlovchilar uchun. Shu sababli Nest bu jarayonni yengillashtiradigan va bir necha qator kod bilan modulning "blueprint"ini tuzishga imkon beradigan ConfigurableModuleBuilder sinfini taqdim etadi.
Masalan, yuqorida ishlatgan misolimizni (ConfigModule) olaylik va uni ConfigurableModuleBuilder dan foydalanishga o'zgartiraylik. Boshlashdan oldin, ConfigModule qabul qiladigan opsiyalarni ifodalaydigan alohida interfeys yaratganimizga ishonch hosil qilaylik.
1export interface ConfigModuleOptions {
2 folder: string;
3}Shu bilan, yangi alohida fayl yarating (mavjud config.module.ts fayli yonida) va uni config.module-definition.ts deb nomlang. Bu faylda ConfigurableModuleBuilder dan foydalanib ConfigModule definitsiyasini tuzamiz.
1import { ConfigurableModuleBuilder } from '@nestjs/common';
2import { ConfigModuleOptions } from './interfaces/config-module-options.interface';
3
4export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
5 new ConfigurableModuleBuilder<ConfigModuleOptions>().build();Endi config.module.ts faylini ochib, uning implementatsiyasini auto-generated ConfigurableModuleClass dan foydalanish uchun o'zgartiramiz:
1import { Module } from '@nestjs/common';
2import { ConfigService } from './config.service';
3import { ConfigurableModuleClass } from './config.module-definition';
4
5@Module({
6 providers: [ConfigService],
7 exports: [ConfigService],
8})
9export class ConfigModule extends ConfigurableModuleClass {}ConfigurableModuleClass ni kengaytirish ConfigModule ga endi faqat register metodini emas (avvalgi maxsus implementatsiyadagi kabi), balki registerAsync metodini ham taqdim etadi. Bu metod consumerlarga modulni asinxron tarzda sozlash imkonini beradi, masalan, asinxron factory'larni uzatish orqali:
1@Module({
2 imports: [
3 ConfigModule.register({ folder: './config' }),
4 // or alternatively:
5 // ConfigModule.registerAsync({
6 // useFactory: () => {
7 // return {
8 // folder: './config',
9 // }
10 // },
11 // inject: [...any extra dependencies...]
12 // }),
13 ],
14})
15export class AppModule {}registerAsync metodi quyidagi obyektni argument sifatida qabul qiladi:
1{
2 /**
3 * Injection token resolving to a class that will be instantiated as a provider.
4 * The class must implement the corresponding interface.
5 */
6 useClass?: Type<
7 ConfigurableModuleOptionsFactory<ModuleOptions, FactoryClassMethodKey>
8 >;
9 /**
10 * Function returning options (or a Promise resolving to options) to configure the
11 * module.
12 */
13 useFactory?: (...args: any[]) => Promise<ModuleOptions> | ModuleOptions;
14 /**
15 * Dependencies that a Factory may inject.
16 */
17 inject?: FactoryProvider['inject'];
18 /**
19 * Injection token resolving to an existing provider. The provider must implement
20 * the corresponding interface.
21 */
22 useExisting?: Type<
23 ConfigurableModuleOptionsFactory<ModuleOptions, FactoryClassMethodKey>
24 >;
25}Endi yuqoridagi xossalarni birma-bir ko'rib chiqamiz:
useFactory- konfiguratsiya obyektini qaytaradigan funksiya. U sinxron yoki asinxron bo'lishi mumkin. Factory funksiyasiga bog'liqliklarni in'eksiya qilish uchuninjectxossasidan foydalaning. Yuqoridagi misolda biz shu variantni ishlatdik.inject- factory funksiyasiga in'eksiya qilinadigan bog'liqliklar massivi. Bog'liqliklar tartibi factory funksiyasidagi parametrlar tartibiga mos bo'lishi kerak.useClass- provayder sifatida instansiyalanadigan sinf. Sinf mos interfeysni amalga oshirishi kerak. Odatda bu konfiguratsiya obyektini qaytaradigancreate()metodini taqdim etadigan sinf. Buning haqida quyidagi Custom method key bo'limida batafsil o'qing.useExisting-useClassning varianti bo'lib, Nestga yangi instansiya yaratishni buyurish o'rniga, mavjud provayderdan foydalanishga imkon beradi. Modulda allaqachon ro'yxatdan o'tgan provayderdan foydalanishni xohlaganingizda foydali. E'tiborda tuting, sinfuseClassda ishlatilgan interfeys bilan bir xil interfeysni amalga oshirishi kerak (shuning uchun ucreate()metodini taqdim etishi shart, agar siz default metod nomini o'zgartirmagan bo'lsangiz; qarang Custom method key bo'limi).
Yuqoridagi variantlardan birini (useFactory, useClass yoki useExisting) har doim tanlang, chunki ular o'zaro mos kelmaydi.
Oxirida, ConfigService sinfini hozirgacha ishlatgan 'CONFIG_OPTIONS' o'rniga generatsiya qilingan modul opsiyalari provayderini in'eksiya qilishga yangilaymiz.
1@Injectable()
2export class ConfigService {
3 constructor(@Inject(MODULE_OPTIONS_TOKEN) private options: ConfigModuleOptions) { ... }
4}Maxsus metod kaliti
ConfigurableModuleClass default holatda register va uning muqobili registerAsync metodlarini taqdim etadi. Boshqa metod nomidan foydalanish uchun ConfigurableModuleBuilder#setClassMethodName metodini quyidagicha ishlating:
1export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
2 new ConfigurableModuleBuilder<ConfigModuleOptions>().setClassMethodName('forRoot').build();Bu konstruktsiya ConfigurableModuleBuilder ga endi register va registerAsync o'rniga forRoot va forRootAsync ni taqdim etadigan sinfni generatsiya qilishni buyuradi. Misol:
1@Module({
2 imports: [
3 ConfigModule.forRoot({ folder: './config' }), // <-- note the use of "forRoot" instead of "register"
4 // or alternatively:
5 // ConfigModule.forRootAsync({
6 // useFactory: () => {
7 // return {
8 // folder: './config',
9 // }
10 // },
11 // inject: [...any extra dependencies...]
12 // }),
13 ],
14})
15export class AppModule {}Maxsus options factory sinfi
registerAsync metodi (yoki konfiguratsiyaga qarab forRootAsync yoki boshqa nom) consumerga modul konfiguratsiyasiga yechiladigan provayder ta'rifini uzatishga imkon beradi, shuning uchun kutubxona iste'molchisi konfiguratsiya obyektini tuzish uchun sinf berishi mumkin.
1@Module({
2 imports: [
3 ConfigModule.registerAsync({
4 useClass: ConfigModuleOptionsFactory,
5 }),
6 ],
7})
8export class AppModule {}Default holatda bu sinf modul konfiguratsiya obyektini qaytaradigan create() metodini taqdim etishi kerak. Biroq, agar kutubxonangiz boshqa nomlash an'anasiga amal qilsa, siz bu xatti-harakatni o'zgartirib, ConfigurableModuleBuilder ga boshqa metodni kutishini aytishingiz mumkin, masalan createConfigOptions, ConfigurableModuleBuilder#setFactoryMethodName metodidan foydalanib:
1export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
2 new ConfigurableModuleBuilder<ConfigModuleOptions>().setFactoryMethodName('createConfigOptions').build();Endi ConfigModuleOptionsFactory sinfi create o'rniga createConfigOptions metodini taqdim etishi shart:
1@Module({
2 imports: [
3 ConfigModule.registerAsync({
4 useClass: ConfigModuleOptionsFactory, // <-- this class must provide the "createConfigOptions" method
5 }),
6 ],
7})
8export class AppModule {}Qo'shimcha opsiyalar
Ba'zi chekka holatlarda modulingiz o'zini qanday tutishini belgilovchi qo'shimcha opsiyalarni qabul qilishi kerak bo'lishi mumkin (bunday opsiyaga yaxshi misol isGlobal bayrog'i - yoki shunchaki global) va shu bilan birga, ular MODULE_OPTIONS_TOKEN provayderiga kiritilmasligi kerak bo'ladi (chunki ular modul ichida ro'yxatdan o'tgan servis/provayderlar uchun ahamiyatsiz, masalan ConfigService host modul global ekanini bilishi shart emas).
Bunday holatlarda ConfigurableModuleBuilder#setExtras metodidan foydalanish mumkin. Quyidagi misolga qarang:
1export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
2 new ConfigurableModuleBuilder<ConfigModuleOptions>()
3 .setExtras(
4 {
5 isGlobal: true,
6 },
7 (definition, extras) => ({
8 ...definition,
9 global: extras.isGlobal,
10 }),
11 )
12 .build();Yuqoridagi misolda setExtras metodiga uzatilgan birinchi argument "extra" xossalar uchun default qiymatlarni o'z ichiga olgan obyekt. Ikkinchi argument - auto-generated modul definitsiyasini (provider, exports va boshqalar bilan) va extras obyektini qabul qiladigan funksiya; extras qo'shimcha xossalarni (consumer tomonidan berilgan yoki default) ifodalaydi. Funksiyaning qaytgan qiymati - o'zgartirilgan modul definitsiyasi. Bu aniq misolda biz extras.isGlobal xossasini modul definitsiyasining global xossasiga biriktiryapmiz (bu esa modul global yoki yo'qligini belgilaydi, batafsil bu yerda).
Endi bu modulni iste'mol qilganda qo'shimcha isGlobal bayrog'ini quyidagicha uzatish mumkin:
1@Module({
2 imports: [
3 ConfigModule.register({
4 isGlobal: true,
5 folder: './config',
6 }),
7 ],
8})
9export class AppModule {}Biroq, isGlobal "extra" xossa sifatida e'lon qilinganligi uchun, u MODULE_OPTIONS_TOKEN provayderida mavjud bo'lmaydi:
1@Injectable()
2export class ConfigService {
3 constructor(
4 @Inject(MODULE_OPTIONS_TOKEN) private options: ConfigModuleOptions,
5 ) {
6 // "options" object will not have the "isGlobal" property
7 // ...
8 }
9}Auto-generated metodlarni kengaytirish
Auto-generated static metodlar (register, registerAsync va hokazo) kerak bo'lsa quyidagicha kengaytirilishi mumkin:
1import { Module } from '@nestjs/common';
2import { ConfigService } from './config.service';
3import {
4 ConfigurableModuleClass,
5 ASYNC_OPTIONS_TYPE,
6 OPTIONS_TYPE,
7} from './config.module-definition';
8
9@Module({
10 providers: [ConfigService],
11 exports: [ConfigService],
12})
13export class ConfigModule extends ConfigurableModuleClass {
14 static register(options: typeof OPTIONS_TYPE): DynamicModule {
15 return {
16 // your custom logic here
17 ...super.register(options),
18 };
19 }
20
21 static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
22 return {
23 // your custom logic here
24 ...super.registerAsync(options),
25 };
26 }
27}Quyida ko'rsatilganidek, modul definitsiya faylidan OPTIONS_TYPE va ASYNC_OPTIONS_TYPE tiplari eksport qilinishi shart:
1export const {
2 ConfigurableModuleClass,
3 MODULE_OPTIONS_TOKEN,
4 OPTIONS_TYPE,
5 ASYNC_OPTIONS_TYPE,
6} = new ConfigurableModuleBuilder<ConfigModuleOptions>().build();