Usullar16 min read

Konfiguratsiya

Ilovalar ko'pincha turli environmentlarda ishlaydi. Environmentga qarab turli konfiguratsiya sozlamalari ishlatilishi kerak. Masalan, odatda local environment faqat local DB instan

Ilovalar ko'pincha turli environmentlarda ishlaydi. Environmentga qarab turli konfiguratsiya sozlamalari ishlatilishi kerak. Masalan, odatda local environment faqat local DB instansiyasi uchun yaroqli bo'lgan maxsus ma'lumotlar bazasi kredensiallariga tayanadi. Production environment esa alohida DB kredensiallar to'plamidan foydalanadi. Konfiguratsiya o'zgaruvchilari o'zgarib turishi sababli, eng yaxshi amaliyot - konfiguratsiya o'zgaruvchilarini environmentda saqlash.

Tashqi aniqlangan environment o'zgaruvchilari Node.js ichida process.env globali orqali ko'rinadi. Bir nechta environment muammosini har bir environmentda environment o'zgaruvchilarini alohida o'rnatish orqali hal qilishga urinib ko'rishimiz mumkin. Bu tezda boshqarib bo'lmas holga keladi, ayniqsa development va testing environmentlarida bu qiymatlarni oson mock qilish va/yoki o'zgartirish kerak bo'lganda.

Node.js ilovalarida .env fayllaridan foydalanish odatiy bo'lib, bunda har bir key muayyan qiymatni ifodalovchi key-value juftliklari saqlanadi va har bir environmentni ifodalaydi. Turli environmentlarda ilovani ishga tushirish shunchaki to'g'ri .env faylini almashtirishdan iborat bo'ladi.

Nestda bu texnikadan foydalanish uchun yaxshi yondashuv - mos .env faylini yuklaydigan ConfigService ni taqdim etuvchi ConfigModule yaratish. Bunday modulni o'zingiz yozishni tanlashingiz mumkin, ammo qulaylik uchun Nest tayyor holda @nestjs/config paketini taqdim etadi. Ushbu bobda aynan shu paketni ko'rib chiqamiz.

O'rnatish

Ishlatishni boshlash uchun avval kerakli bog'liqlikni o'rnating.

Terminal
1$ npm i --save @nestjs/config
Hint

@nestjs/config paketi ichkarida dotenv dan foydalanadi.

Note

@nestjs/config TypeScript 4.1 yoki undan yuqorini talab qiladi.

Boshlash

O'rnatish jarayoni tugagach, ConfigModule ni import qilamiz. Odatda uni root AppModule ga import qilib, .forRoot() statik metodi bilan xatti-harakatini boshqaramiz. Ushbu bosqichda environment o'zgaruvchilarining key/value juftliklari parse qilinadi va yechiladi. Keyinroq ConfigModule ning ConfigService sinfiga boshqa feature modullardan qanday kirishni bir nechta usulda ko'ramiz.

TypeScript
app.module
1import { Module } from '@nestjs/common';
2import { ConfigModule } from '@nestjs/config';
3
4@Module({
5  imports: [ConfigModule.forRoot()],
6})
7export class AppModule {}

Yuqoridagi kod default joylashuvdan (project root katalogi) .env faylini yuklaydi va parse qiladi, .env faylidagi key/value juftliklarini process.env ga berilgan environment o'zgaruvchilari bilan birlashtiradi va natijani ConfigService orqali kirish mumkin bo'lgan xususiy tuzilmada saqlaydi. forRoot() metodi ConfigService provayderini ro'yxatdan o'tkazadi; u ushbu parse qilingan/birlashtirilgan konfiguratsiya o'zgaruvchilarini o'qish uchun get() metodini taqdim etadi. @nestjs/config dotenv ga tayanadiganligi sababli, environment o'zgaruvchi nomlaridagi ziddiyatlarni hal qilishda o'sha paket qoidalaridan foydalanadi. Agar key runtime environmentda environment o'zgaruvchisi sifatida (masalan, OS shell exportlari orqali export DATABASE_USER=test) ham, .env faylida ham mavjud bo'lsa, runtime environmentdagi o'zgaruvchi ustunlik qiladi.

Namunaviy .env fayli quyidagicha ko'rinadi:

JSON
1DATABASE_USER=test
2DATABASE_PASSWORD=test

Agar ayrim env o'zgaruvchilar ConfigModule yuklanishidan va Nest ilovasi bootstrapping qilinishidan oldin ham mavjud bo'lishi kerak bo'lsa (masalan, NestFactory#createMicroservice metodiga mikroservis konfiguratsiyasini uzatish uchun), Nest CLI ning --env-file opsiyasidan foydalanishingiz mumkin. Bu opsiya ilova ishga tushishidan oldin yuklanishi kerak bo'lgan .env fayl pathini ko'rsatish imkonini beradi. --env-file flag qo'llab-quvvatlashi Node v20 da joriy etilgan, batafsil ma'lumot uchun hujjatlarga qarang.

Terminal
1$ nest start --env-file .env

Maxsus env fayl pathi

Standart holatda paket ilovaning root katalogida .env faylini qidiradi. .env fayli uchun boshqa path ko'rsatish uchun forRoot() ga uzatiladigan (ixtiyoriy) opsiyalar obyektidagi envFilePath xossasini sozlang, quyidagicha:

TypeScript
1ConfigModule.forRoot({
2  envFilePath: '.development.env',
3});

.env fayllari uchun bir nechta pathni quyidagicha ko'rsatishingiz mumkin:

TypeScript
1ConfigModule.forRoot({
2  envFilePath: ['.env.development.local', '.env.development'],
3});

Agar o'zgaruvchi bir nechta faylda topilsa, birinchisi ustunlik qiladi.

Env o'zgaruvchilarini yuklashni o'chirish

Agar .env faylini yuklamoqchi bo'lmasangiz, balki runtime environmentdan (masalan, OS shell exportlari export DATABASE_USER=test) environment o'zgaruvchilariga kirishni xohlasangiz, opsiyalar obyektidagi ignoreEnvFile xossasini true qilib qo'ying, quyidagicha:

TypeScript
1ConfigModule.forRoot({
2  ignoreEnvFile: true,
3});

Modulni global ishlatish

ConfigModule ni boshqa modullarda ishlatmoqchi bo'lsangiz, uni import qilishingiz kerak (istalgan Nest modulida bo'lgani kabi). Muqobil ravishda, opsiyalar obyektidagi isGlobal xossasini true qilib, uni global modul sifatida e'lon qilishingiz mumkin. Bunda ConfigModule root modulda (masalan, AppModule) yuklangach, boshqa modullarda uni import qilishingiz shart bo'lmaydi.

TypeScript
1ConfigModule.forRoot({
2  isGlobal: true,
3});

Maxsus konfiguratsiya fayllari

Murakkabroq loyihalar uchun nested konfiguratsiya obyektlarini qaytaradigan maxsus konfiguratsiya fayllaridan foydalanishingiz mumkin. Bu bog'liq konfiguratsiya sozlamalarini funksiyaga ko'ra guruhlash imkonini beradi (masalan, DBga oid sozlamalar), hamda bog'liq sozlamalarni alohida fayllarda saqlab, ularni mustaqil boshqarishni osonlashtiradi.

Maxsus konfiguratsiya fayli konfiguratsiya obyektini qaytaradigan factory funksiyani eksport qiladi. Konfiguratsiya obyekti istalgan darajada nested bo'lgan oddiy JavaScript obyekt bo'lishi mumkin. process.env obyekti to'liq yechilgan environment variable key/value juftliklarini o'z ichiga oladi (.env fayli va tashqi aniqlangan o'zgaruvchilar yuqorida ta'riflanganidek yechilib va birlashtiriladi). Qaytariladigan konfiguratsiya obyektini siz nazorat qilganingiz sababli, qiymatlarni mos tipga cast qilish, default qiymatlar berish va hokazo kabi kerakli mantiqni qo'shishingiz mumkin. Masalan:

TypeScript
config/configuration
1export default () => ({
2  port: parseInt(process.env.PORT, 10) || 3000,
3  database: {
4    host: process.env.DATABASE_HOST,
5    port: parseInt(process.env.DATABASE_PORT, 10) || 5432
6  }
7});

Bu faylni ConfigModule.forRoot() metodiga uzatadigan opsiyalar obyektidagi load xossasi orqali yuklaymiz:

TypeScript
1import configuration from './config/configuration';
2
3@Module({
4  imports: [
5    ConfigModule.forRoot({
6      load: [configuration],
7    }),
8  ],
9})
10export class AppModule {}
Notice

load xossasiga berilgan qiymat massivdir, bu bir nechta konfiguratsiya faylini yuklash imkonini beradi (masalan, load: [databaseConfig, authConfig])

Maxsus konfiguratsiya fayllari bilan YAML fayllarini ham boshqarishingiz mumkin. Quyida YAML formatidagi konfiguratsiya misoli:

YAML
1http:
2  host: 'localhost'
3  port: 8080
4
5db:
6  postgres:
7    url: 'localhost'
8    port: 5432
9    database: 'yaml-db'
10
11  sqlite:
12    database: 'sqlite.db'

YAML fayllarini o'qish va parse qilish uchun js-yaml paketidan foydalanamiz.

Terminal
1$ npm i js-yaml
2$ npm i -D @types/js-yaml

Paket o'rnatilgach, yuqorida yaratgan YAML faylini yuklash uchun yaml#load funksiyasidan foydalanamiz.

TypeScript
config/configuration
1import { readFileSync } from 'node:fs';
2import { join } from 'node:path';
3import * as yaml from 'js-yaml';
4
5const YAML_CONFIG_FILENAME = 'config.yaml';
6
7export default () => {
8  return yaml.load(
9    readFileSync(join(__dirname, YAML_CONFIG_FILENAME), 'utf8'),
10  ) as Record<string, any>;
11};
Note

Nest CLI build jarayonida "assets" (TS bo'lmagan fayllar)ni avtomatik ravishda dist papkasiga ko'chirmaydi. YAML fayllaringiz ko'chirilishini ta'minlash uchun nest-cli.json faylida compilerOptions#assets obyektida buni ko'rsatishingiz kerak. Masalan, agar config papkasi src papkasi bilan bir xil darajada bo'lsa, compilerOptions#assets ga "assets": [{{ '{' }}"include": "../config/*.yaml", "outDir": "./dist/config"{{ '}' }}] qiymatini qo'shing. Batafsil bu yerda.

Qisqa eslatma: konfiguratsiya fayllari avtomatik validatsiya qilinmaydi, hatto NestJS ConfigModuleida validationSchema opsiyasidan foydalansangiz ham. Agar validatsiya kerak bo'lsa yoki transformatsiyalarni qo'llamoqchi bo'lsangiz, buni konfiguratsiya obyektini to'liq nazorat qiladigan factory funksiya ichida bajarishingiz kerak. Bu kerakli custom validatsiya mantiqini amalga oshirishga imkon beradi.

Masalan, port ma'lum bir diapazonda ekanini ta'minlash uchun factory funksiyasiga validatsiya qadamini qo'shishingiz mumkin:

TypeScript
config/configuration
1export default () => {
2  const config = yaml.load(
3    readFileSync(join(__dirname, YAML_CONFIG_FILENAME), 'utf8'),
4  ) as Record<string, any>;
5
6  if (config.http.port < 1024 || config.http.port > 49151) {
7    throw new Error('HTTP port must be between 1024 and 49151');
8  }
9
10  return config;
11};

Endi port ko'rsatilgan diapazondan tashqarida bo'lsa, ilova ishga tushishda xato tashlaydi.

ConfigServicedan foydalanish

ConfigService dan konfiguratsiya qiymatlarini olish uchun avvalo ConfigService ni in'eksiya qilishimiz kerak. Istalgan provayder kabi, uni ishlatadigan modulga o'zini o'z ichiga olgan ConfigModule ni import qilishimiz kerak (agar ConfigModule.forRoot() ga uzatiladigan opsiyalar obyektida isGlobal xossasini true qilmagan bo'lsangiz). Uni feature modulga quyidagicha import qiling.

TypeScript
feature.module
1@Module({
2  imports: [ConfigModule],
3  // ...
4})

So'ng uni odatiy konstruktor in'eksiyasi orqali kiriting:

TypeScript
1constructor(private configService: ConfigService) {}
Hint

ConfigService@nestjs/config paketidan import qilinadi.

Va sinfda undan foydalaning:

TypeScript
1// get an environment variable
2const dbUser = this.configService.get<string>('DATABASE_USER');
3
4// get a custom configuration value
5const dbHost = this.configService.get<string>('database.host');

Yuqorida ko'rsatilganidek, oddiy environment o'zgaruvchisini olish uchun configService.get() metodidan foydalanib o'zgaruvchi nomini uzating. Yuqorida ko'rsatilgandek, TypeScript type hinting uchun tipni uzatishingiz mumkin (masalan, get<string>(...)). get() metodi nested custom konfiguratsiya obyektini ham ko'ra oladi (Custom configuration file orqali yaratilgan), bu yuqoridagi ikkinchi misolda ko'rsatilgan.

Siz butun nested custom konfiguratsiya obyektini interfeysni type hint sifatida berib ham olishingiz mumkin:

TypeScript
1interface DatabaseConfig {
2  host: string;
3  port: number;
4}
5
6const dbConfig = this.configService.get<DatabaseConfig>('database');
7
8// you can now use `dbConfig.port` and `dbConfig.host`
9const port = dbConfig.port;

get() metodi ixtiyoriy ikkinchi argumentni ham qabul qiladi, u default qiymatni belgilaydi va key mavjud bo'lmasa qaytariladi, quyidagicha:

TypeScript
1// use "localhost" when "database.host" is not defined
2const dbHost = this.configService.get<string>('database.host', 'localhost');

ConfigService ikkita ixtiyoriy generic (tip argumenti)ga ega. Birinchisi mavjud bo'lmagan config xossasiga kirishni oldini olishga yordam beradi. Uni quyidagicha ishlating:

TypeScript
1interface EnvironmentVariables {
2  PORT: number;
3  TIMEOUT: string;
4}
5
6// somewhere in the code
7constructor(private configService: ConfigService<EnvironmentVariables>) {
8  const port = this.configService.get('PORT', { infer: true });
9
10  // TypeScript Error: this is invalid as the URL property is not defined in EnvironmentVariables
11  const url = this.configService.get('URL', { infer: true });
12}

infer xossasi true bo'lsa, ConfigService#get metodi interfeys asosida xossa tipini avtomatik aniqlaydi, masalan PORT EnvironmentVariables interfeysida number tipi bo'lgani uchun typeof port === "number" (agar TypeScriptning strictNullChecks flagidan foydalanmayotgan bo'lsangiz).

Shuningdek, infer imkoniyati bilan, dot notation ishlatilganda ham nested custom konfiguratsiya obyektining xossasi tipini aniqlashingiz mumkin, quyidagicha:

TypeScript
1constructor(private configService: ConfigService<{ database: { host: string } }>) {
2  const dbHost = this.configService.get('database.host', { infer: true })!;
3  // typeof dbHost === "string"                                          |
4  //                                                                     +--> non-null assertion operator
5}

Ikkinchi generic birinchisiga tayanadi va strictNullChecks yoqilganda ConfigService metodlari qaytarishi mumkin bo'lgan barcha undefined tiplarni olib tashlash uchun type assertion vazifasini bajaradi. Masalan:

TypeScript
1// ...
2constructor(private configService: ConfigService<{ PORT: number }, true>) {
3  //                                                               ^^^^
4  const port = this.configService.get('PORT', { infer: true });
5  //    ^^^ The type of port will be 'number' thus you don't need TS type assertions anymore
6}
Hint

ConfigService#get metodi qiymatlarni faqat custom konfiguratsiya fayllaridan olishini va process.env o'zgaruvchilarini e'tiborsiz qoldirishini istasangiz, ConfigModule ning forRoot() metodiga uzatiladigan opsiyalar obyektida skipProcessEnv opsiyasini true qilib qo'ying.

Konfiguratsiya namespace'lari

ConfigModule yuqorida Custom configuration files bo'limida ko'rsatilganidek, bir nechta maxsus konfiguratsiya fayllarini aniqlash va yuklash imkonini beradi. O'sha bo'limda ko'rsatilgandek nested konfiguratsiya obyektlari bilan murakkab konfiguratsiya ierarxiyalarini boshqarishingiz mumkin. Muqobil ravishda, registerAs() funksiyasi bilan "namespaced" konfiguratsiya obyektini qaytarishingiz mumkin, quyidagicha:

TypeScript
config/database.config
1export default registerAs('database', () => ({
2  host: process.env.DATABASE_HOST,
3  port: process.env.DATABASE_PORT || 5432
4}));

Custom konfiguratsiya fayllarida bo'lgani kabi, registerAs() factory funksiyasi ichida process.env obyekti to'liq yechilgan environment variable key/value juftliklarini o'z ichiga oladi (.env fayli va tashqi aniqlangan o'zgaruvchilar yuqorida ta'riflanganidek yechilib va birlashtiriladi).

Hint

registerAs funksiyasi @nestjs/config paketidan eksport qilinadi.

Namespaced konfiguratsiyani forRoot() metodining opsiyalar obyektidagi load xossasi orqali, custom konfiguratsiya faylini yuklagandek, yuklang:

TypeScript
1import databaseConfig from './config/database.config';
2
3@Module({
4  imports: [
5    ConfigModule.forRoot({
6      load: [databaseConfig],
7    }),
8  ],
9})
10export class AppModule {}

Endi database namespace'dan host qiymatini olish uchun dot notationdan foydalaning. registerAs() funksiyasiga birinchi argument sifatida berilgan namespace nomiga mos ravishda, xossa nomidan oldin 'database' prefiksini ishlating:

TypeScript
1const dbHost = this.configService.get<string>('database.host');

Yana bir maqbul variant - database namespace'ni bevosita in'eksiya qilish. Bu strong typingdan foydalanish imkonini beradi:

TypeScript
1constructor(
2  @Inject(databaseConfig.KEY)
3  private dbConfig: ConfigType<typeof databaseConfig>,
4) {}
Hint

ConfigType@nestjs/config paketidan eksport qilinadi.

Modullarda namespaced konfiguratsiyalar

Namespaced konfiguratsiyani ilovangizdagi boshqa modul uchun konfiguratsiya obyektiga aylantirish uchun konfiguratsiya obyektining .asProvider() metodidan foydalanishingiz mumkin. Bu metod namespaced konfiguratsiyani provayderga aylantiradi va uni ishlatmoqchi bo'lgan modulning forRootAsync() (yoki boshqa ekvivalent metodiga) uzatish mumkin.

Misol:

TypeScript
1import databaseConfig from './config/database.config';
2
3@Module({
4  imports: [
5    TypeOrmModule.forRootAsync(databaseConfig.asProvider()),
6  ],
7})

.asProvider() metodi qanday ishlashini tushunish uchun uning qaytadigan qiymatini ko'rib chiqamiz:

TypeScript
1// Return value of the .asProvider() method
2{
3  imports: [ConfigModule.forFeature(databaseConfig)],
4  useFactory: (configuration: ConfigType<typeof databaseConfig>) => configuration,
5  inject: [databaseConfig.KEY]
6}

Ushbu tuzilma namespaced konfiguratsiyalarni modullarga muammosiz integratsiya qilishga imkon beradi, ilovangizni tartibli va modulli holatda ushlab, boilerplate va takroriy kod yozmasdan.

Environment o'zgaruvchilarini keshlash

process.env ga kirish sekin bo'lishi mumkinligi sababli, ConfigService#get metodi process.env dagi o'zgaruvchilar uchun ishlaganda performance oshishi uchun ConfigModule.forRoot() ga uzatiladigan opsiyalar obyektida cache xossasini true qilib qo'yishingiz mumkin.

TypeScript
1ConfigModule.forRoot({
2  cache: true,
3});

Qisman ro'yxatdan o'tkazish

Hozirgacha konfiguratsiya fayllarini root modulimizda (masalan, AppModule) forRoot() metodi bilan qayta ishladik. Balki sizda murakkabroq loyiha tuzilmasi bor, feature'ga xos konfiguratsiya fayllari bir nechta kataloglarda joylashgan. Bu fayllarning barchasini root modulda yuklash o'rniga, @nestjs/config paketi partial registration deb ataladigan imkoniyatni taqdim etadi; u har bir feature modulga tegishli konfiguratsiya fayllarinigina referenslaydi. Qisman ro'yxatdan o'tkazishni feature modulda forFeature() statik metodi bilan bajaring, quyidagicha:

TypeScript
1import databaseConfig from './config/database.config';
2
3@Module({
4  imports: [ConfigModule.forFeature(databaseConfig)],
5})
6export class DatabaseModule {}
Warning

Ba'zi holatlarda, qisman ro'yxatdan o'tkazilgan xossalarga konstruktor ichida emas, onModuleInit() hook'i orqali kirish talab qilinishi mumkin. Buning sababi forFeature() metodi modul inicializatsiyasi vaqtida ishlaydi va modullar inicializatsiyasi tartibi noaniq bo'lishi mumkin. Agar boshqa modul yuklagan qiymatlarga konstruktor ichida kirilsa, konfiguratsiya bog'liq bo'lgan modul hali inicializatsiya bo'lmagan bo'lishi mumkin. onModuleInit() metodi faqat bog'liq bo'lgan barcha modullar inicializatsiya bo'lgandan keyin ishlaydi, shuning uchun bu usul xavfsiz.

Schema validatsiyasi

Kerakli environment o'zgaruvchilari berilmagan yoki ular muayyan validatsiya qoidalariga mos kelmagan bo'lsa, ilova ishga tushishda istisno tashlash standard amaliyot hisoblanadi. @nestjs/config paketi buni ikki xil yo'l bilan qilishga imkon beradi:

  • Joi o'rnatilgan validator. Joi bilan siz obyekt sxemasini aniqlab, JavaScript obyektlarini unga nisbatan validatsiya qilasiz.
  • Environment o'zgaruvchilarini input sifatida oladigan maxsus validate() funksiyasi.

Joi'dan foydalanish uchun Joi paketini o'rnatishimiz kerak:

Terminal
1$ npm install --save joi

Endi Joi validatsiya sxemasini aniqlab, uni forRoot() metodining opsiyalar obyektidagi validationSchema xossasi orqali uzatamiz, quyida ko'rsatilganidek:

TypeScript
app.module
1import * as Joi from 'joi';
2
3@Module({
4  imports: [
5    ConfigModule.forRoot({
6      validationSchema: Joi.object({
7        NODE_ENV: Joi.string()
8          .valid('development', 'production', 'test', 'provision')
9          .default('development'),
10        PORT: Joi.number().port().default(3000),
11      }),
12    }),
13  ],
14})
15export class AppModule {}

Standart holatda sxema kalitlarining barchasi ixtiyoriy deb hisoblanadi. Bu yerda biz NODE_ENV va PORT uchun default qiymatlarni berdik; agar bu o'zgaruvchilarni environmentda taqdim etmasak (.env fayli yoki process environment), shu qiymatlar ishlatiladi. Muqobil ravishda, required() validatsiya metodidan foydalanib qiymat environmentda majburiy bo'lishini talab qilishingiz mumkin (.env fayli yoki process environment). Bu holatda validatsiya bosqichi o'zgaruvchi berilmagan bo'lsa istisno tashlaydi. Validatsiya sxemalarini qanday qurish bo'yicha batafsil Joi validation methodsga qarang.

Standart holatda noma'lum environment o'zgaruvchilari (kalitlari sxemada mavjud bo'lmaganlari) ruxsat etiladi va validatsiya istisnosini keltirib chiqarmaydi. Standart holatda barcha validatsiya xatolari qayd etiladi. Bu xatti-harakatlarni forRoot() opsiyalar obyektidagi validationOptions kaliti orqali opsiyalar obyektini uzatib o'zgartirishingiz mumkin. Bu opsiyalar obyektida Joi validation options taqdim etadigan standard validatsiya opsiyalari bo'lishi mumkin. Masalan, yuqoridagi ikki sozlamani teskari qilish uchun quyidagicha opsiyalar uzating:

TypeScript
app.module
1import * as Joi from 'joi';
2
3@Module({
4  imports: [
5    ConfigModule.forRoot({
6      validationSchema: Joi.object({
7        NODE_ENV: Joi.string()
8          .valid('development', 'production', 'test', 'provision')
9          .default('development'),
10        PORT: Joi.number().port().default(3000),
11      }),
12      validationOptions: {
13        allowUnknown: false,
14        abortEarly: true,
15      },
16    }),
17  ],
18})
19export class AppModule {}

@nestjs/config paketining default sozlamalari:

  • allowUnknown: environment o'zgaruvchilarida noma'lum kalitlarga ruxsat berish-bermasligini boshqaradi. Default true
  • abortEarly: true bo'lsa birinchi xatoda validatsiyani to'xtatadi; false bo'lsa barcha xatolarni qaytaradi. Default false.

E'tibor bering, validationOptions obyektini uzatishga qaror qilsangiz, aniq berilmagan har qanday sozlama Joining standard default qiymatlariga o'rnatiladi (@nestjs/config defaultlari emas). Masalan, custom validationOptions obyektida allowUnknown ko'rsatilmasa, u Joi default qiymati false bo'ladi. Shu sababli custom obyekt ichida bu ikki sozlamaning ikkalasini ham ko'rsatish xavfsizroq bo'ladi.

Hint

Oldindan belgilangan environment o'zgaruvchilarini validatsiya qilishni o'chirish uchun forRoot() metodining opsiyalar obyektida validatePredefined atributini false ga o'rnating. Oldindan belgilangan environment o'zgaruvchilari - modul import qilinishidan oldin o'rnatilgan process o'zgaruvchilari (process.env o'zgaruvchilari). Masalan, ilovani PORT=3000 node main.js bilan ishga tushirsangiz, PORT oldindan belgilangan environment o'zgaruvchisidir.

Maxsus validate funksiyasi

Muqobil ravishda, env fayli va processdan olingan environment o'zgaruvchilarini o'z ichiga olgan obyektni qabul qiladigan va zarur bo'lsa ularni konvertatsiya/mutatsiya qiladigan, validatsiyalangan environment o'zgaruvchilarini qaytaradigan sinxron validate funksiyasini ko'rsatishingiz mumkin. Funksiya xato tashlasa, ilova bootstrapping qilinmaydi.

Bu misolda class-transformer va class-validator paketlaridan foydalanamiz. Avval quyidagilarni aniqlaymiz:

  • validatsiya cheklovlariga ega sinf,
  • plainToInstance va validateSync funksiyalaridan foydalanadigan validate funksiyasi.
TypeScript
env.validation
1import { plainToInstance } from 'class-transformer';
2import { IsEnum, IsNumber, Max, Min, validateSync } from 'class-validator';
3
4enum Environment {
5  Development = "development",
6  Production = "production",
7  Test = "test",
8  Provision = "provision",
9}
10
11class EnvironmentVariables {
12  @IsEnum(Environment)
13  NODE_ENV: Environment;
14
15  @IsNumber()
16  @Min(0)
17  @Max(65535)
18  PORT: number;
19}
20
21export function validate(config: Record<string, unknown>) {
22  const validatedConfig = plainToInstance(
23    EnvironmentVariables,
24    config,
25    { enableImplicitConversion: true },
26  );
27  const errors = validateSync(validatedConfig, { skipMissingProperties: false });
28
29  if (errors.length > 0) {
30    throw new Error(errors.toString());
31  }
32  return validatedConfig;
33}

Shu bilan, validate funksiyasini ConfigModule konfiguratsiya opsiyasi sifatida ishlating, quyidagicha:

TypeScript
app.module
1import { validate } from './env.validation';
2
3@Module({
4  imports: [
5    ConfigModule.forRoot({
6      validate,
7    }),
8  ],
9})
10export class AppModule {}

Maxsus getter funksiyalari

ConfigService kalit bo'yicha konfiguratsiya qiymatini olish uchun generik get() metodini taqdim etadi. Shuningdek, yanada tabiiy kodlash uslubi uchun getter funksiyalarini ham qo'shishimiz mumkin:

TypeScript
1@Injectable()
2export class ApiConfigService {
3  constructor(private configService: ConfigService) {}
4
5  get isAuthEnabled(): boolean {
6    return this.configService.get('AUTH_ENABLED') === 'true';
7  }
8}

Endi getter funksiyasidan quyidagicha foydalanishimiz mumkin:

TypeScript
app.service
1@Injectable()
2export class AppService {
3  constructor(apiConfigService: ApiConfigService) {
4    if (apiConfigService.isAuthEnabled) {
5      // Authentication is enabled
6    }
7  }
8}

Environment o'zgaruvchilari yuklangan hook

Agar modul konfiguratsiyasi environment o'zgaruvchilariga bog'liq bo'lsa va bu o'zgaruvchilar .env faylidan yuklansa, process.env obyektiga murojaat qilishdan oldin fayl yuklanganini ta'minlash uchun ConfigModule.envVariablesLoaded hook'idan foydalanishingiz mumkin, quyidagi misolga qarang:

TypeScript
1export async function getStorageModule() {
2  await ConfigModule.envVariablesLoaded;
3  return process.env.STORAGE === 'S3' ? S3StorageModule : DefaultStorageModule;
4}

Bu konstruktsiya ConfigModule.envVariablesLoaded Promise resolve bo'lgach, barcha konfiguratsiya o'zgaruvchilari yuklanganini kafolatlaydi.

Shartli modul konfiguratsiyasi

Ba'zan modulni shartli tarzda yuklash va shartni env o'zgaruvchisida ko'rsatish kerak bo'ladi. Yaxshiyamki, @nestjs/config buni qilish uchun ConditionalModule ni taqdim etadi.

TypeScript
1@Module({
2  imports: [
3    ConfigModule.forRoot(),
4    ConditionalModule.registerWhen(FooModule, 'USE_FOO'),
5  ],
6})
7export class AppModule {}

Yuqoridagi modul .env faylida USE_FOO env o'zgaruvchisi uchun false qiymati bo'lmasa, FooModule ni yuklaydi. Siz o'zingizning shart funksiyangizni ham uzatishingiz mumkin; bu funksiya process.env havolasini qabul qiladi va ConditionalModule ishlatishi uchun boolean qaytarishi kerak:

TypeScript
1@Module({
2  imports: [
3    ConfigModule.forRoot(),
4    ConditionalModule.registerWhen(
5      FooBarModule,
6      (env: NodeJS.ProcessEnv) => !!env['foo'] && !!env['bar'],
7    ),
8  ],
9})
10export class AppModule {}

ConditionalModule dan foydalanganda ConfigModule ham ilovada yuklanganiga ishonch hosil qilish muhim, shunda ConfigModule.envVariablesLoaded hook'i to'g'ri referenslanadi va ishlatiladi. Agar hook 5 soniya ichida true bo'lib o'zgarmasa yoki registerWhen metodining uchinchi opsiya parametrida foydalanuvchi tomonidan millisekundlarda belgilangan timeout tugasa, ConditionalModule xato tashlaydi va Nest ilovani ishga tushirishni bekor qiladi.

Kengaytiriladigan o'zgaruvchilar

@nestjs/config paketi environment o'zgaruvchilarini kengaytirishni qo'llab-quvvatlaydi. Bu texnika yordamida nested environment o'zgaruvchilarini yaratishingiz mumkin, bunda bitta o'zgaruvchi boshqasining ta'rifida ishlatiladi. Masalan:

JSON
1APP_URL=mywebsite.com
2SUPPORT_EMAIL=support@${APP_URL}

Ushbu konstruktsiyada SUPPORT_EMAIL o'zgaruvchisi 'support@mywebsite.com' ga yechiladi. ${{ '{' }}...{{ '}' }} sintaksisidan foydalanib SUPPORT_EMAIL ta'rifida APP_URL qiymatini yechish yoqilganiga e'tibor bering.

Hint

Bu imkoniyat uchun @nestjs/config paketi ichkarida dotenv-expand dan foydalanadi.

Environment o'zgaruvchilarini kengaytirishni ConfigModule ning forRoot() metodiga uzatiladigan opsiyalar obyektida expandVariables xossasi orqali yoqing, quyida ko'rsatilgandek:

TypeScript
app.module
1@Module({
2  imports: [
3    ConfigModule.forRoot({
4      // ...
5      expandVariables: true,
6    }),
7  ],
8})
9export class AppModule {}

main.ts ichida foydalanish

Konfiguratsiyamiz servis ichida saqlansa ham, uni main.ts faylida ishlatish mumkin. Shu tarzda ilova porti yoki CORS host kabi o'zgaruvchilarni saqlashda foydalanishingiz mumkin.

Unga kirish uchun app.get() metodidan, so'ng servis havolasidan foydalanishingiz kerak:

TypeScript
1const configService = app.get(ConfigService);

So'ng get metodini konfiguratsiya kaliti bilan chaqirib, odatdagidek foydalanishingiz mumkin:

TypeScript
1const port = configService.get('PORT');