8.14-bob: Config moduli — env, validatsiya, namespaced config
8-QISM — NestJS (chuqur) · 14-mavzu
1. Kirish va motivatsiya
MongoDB'ni 8.13-bob bildik. Endi shu paytgacha deyarli har bobda ishlatgan ConfigServiceni — NestJS'ning konfiguratsiya tizimini — chuqur o'rganamiz. 5.8'da dotenv va env o'zgaruvchilarini Express'da ko'rdik; endi NestJS'da @nestjs/config bilan — professional, validatsiyalangan, tur xavfsiz, markazlashtirilgan konfiguratsiya. Bu bob barcha sozlamalarni (DB URI, JWT secret, SMTP, AWS, bot token — 8.3, 8.9, 8.10, 8.12, 8.13) bir tizimga jamlaydi.
Konfiguratsiya — ilovaning muhitga bog'liq qiymatlari: DB manzili, parollar, API kalitlar, portlar. Bular kodda bo'lmasligi kerak (12-faktorli ilova printsipi — 5.8): har muhit (dev/test/prod) har xil qiymat, va sirlar (secret) kodga tushmasligi shart (14). NestJS @nestjs/config — .env fayldan o'qiydi, validatsiya qiladi (Joi — noto'g'ri/yo'q sozlama ilova ishga tushmaydi, runtime xato emas!), va ConfigService orqali DI bilan beradi.
Bu bob — promptdagi mavzuni chuqurlashtiradi: ConfigModule (isGlobal), env validatsiya (Joi schema), namespaced config (registerAs — modulli sozlama), tur xavfsiz config, turli muhit (dev/prod), va xavfsizlik (sirlar). Bu bob: ConfigModule, validatsiya, namespace, tur xavfsizlik — chuqur. To'g'ri config — barqaror, xavfsiz, ko'chiriladigan ilova asosi.
O'xshatish: config — uskunaning sozlama paneli (5.8: rozetka). Lekin bu bobda biz uni professional qilamiz: panel tekshiruvi bilan (validatsiya — noto'g'ri sozlama bilan uskuna yoqilmaydi, ishlab turib buzilmaydi); bo'limlarga ajratilgan (namespace — "DB sozlamalari", "Auth sozlamalari" alohida panellar); va har joy uchun alohida profil (dev/prod — uy/zavod sozlamalari). Sozlama panelisiz uskuna (hardcode) — har o'zgarishda korpusni ochish (kodga tegish).
Nega muhim?
- Muhitga bog'liq — dev/prod har xil (DB, kalit — 5.8).
- Sirlar himoyasi — kalit/parol kodda emas (14).
- Validatsiya — noto'g'ri config darrov xato (runtime emas).
- Markazlashtirilgan — barcha sozlama bir tizim (DI).
2. Nazariya — chuqur tushuntirish
2.1. ConfigModule sozlash (asos)
// app.module.ts
import { ConfigModule } from "@nestjs/config";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // butun ilovaga (qayta import kerak emas)
envFilePath: ".env", // env fayl
cache: true, // tezlik (cache)
}),
],
})
export class AppModule {}ConfigModule.forRoot:
.envfaylni o'qiydi,process.envga qo'yadi.isGlobal: true— butun ilovaga (har modulda qayta import kerak emas — 8.1).cache: true(tezlik). Bir marta (root). Bu — 5.8 dotenv'ning NestJS versiyasi (DI + global).
forRoot asosiy opsiyalari (to'liq):
ConfigModule.forRoot({
isGlobal: true, // butun ilovaga (qayta import shart emas)
envFilePath: [".env.local", ".env"], // bir nechta fayl — birinchisi ustun (birlashadi)
ignoreEnvFile: false, // true — .env faylni o'qimaydi, faqat process.env (prod/Docker)
ignoreEnvVars: false, // true — process.env'ni e'tiborsiz qoldiradi (faqat fayl/load)
cache: true, // qiymatlarni keshlaydi (har o'qishda process.env'ga tegmaydi — tezroq)
expandVariables: true, // .env ichida ${...} kengaytirish (o'zgaruvchini o'zgaruvchida)
load: [databaseConfig], // namespaced config fayllar (2.5, 2.6)
validationSchema: Joi.object({}), // env validatsiya (2.3)
});
forRootopsiyalari batafsil:
envFilePath— bitta yo'l (".env") yoki massiv ([".env.local", ".env"]). Massivda birinchi fayl ustun turadi (bir xil kalit bo'lsa, oldingisi g'olib) — lokal override uchun qulay.ignoreEnvFile: true—.envfaylni umuman o'qimaydi, faqatprocess.envdan oladi. Productionda odatiy (env server/Docker/CI'dan keladi — 10, 2.8), disk'da fayl yo'q.ignoreEnvVars: true— aksincha,process.envni e'tiborsiz qoldiradi va faqatload/fayldan oladi (kam ishlatiladi — test izolyatsiyasida).cache: true— o'qilgan qiymatlarni ichki obyektda saqlaydi.process.env— sekin (har chaqiruvda qidiruv), keshlangangettezroq. Ishga tushgachprocess.envo'zgarsa, kesh yangilanmaydi (odatda muammo emas).expandVariables: true—.envichida o'zgaruvchini boshqa o'zgaruvchida ishlatish (dotenv-expand). Masalan:bash# .env APP_HOST=localhost APP_PORT=3000 APP_URL=http://${APP_HOST}:${APP_PORT} # http://localhost:3000 (expandVariables kerak)
infer: true(kam ishlatiladi) —getda generic bermasangiz ham qiymat turini avtomatik aniqlashga urinadi.
2.2. ConfigService (qiymat olish)
@Injectable()
export class AppService {
constructor(private config: ConfigService) {} // DI (8.2)
example() {
const port = this.config.get<number>("PORT"); // qiymat (undefined bo'lishi mumkin)
const dbUri = this.config.get<string>("DATABASE_URI");
const secret = this.config.get("JWT_SECRET", "default"); // default qiymat (2-argument)
// getOrThrow — yo'q bo'lsa istisno tashlaydi (undefined qaytarmaydi)
const uri = this.config.getOrThrow<string>("DATABASE_URI");
}
}ConfigService — DI bilan qiymat olish:
config.get<T>("KEY")(tur bilan), ikkinchi argument — default. isGlobal bo'lsa, har joyda inject. Bu — 8.3, 8.9, 8.10 daforRootAsyncda ishlatganConfigService.process.envto'g'ridan o'qish o'rniga ConfigService (markazlashtirilgan, tur, validatsiya).
getvagetOrThrowfarqi:get— kalit yo'q bo'lsaundefinedqaytaradi (yoki default).getOrThrow— kalit yo'q bo'lsa istisno tashlaydi (Configuration key "X" does not exist). Majburiy sozlamalar uchungetOrThrowafzal:const uri = config.getOrThrow<string>("DATABASE_URI")— endi TypeScripturinistringdeb biladi (string | undefinedemas), va yo'q bo'lsa darrov xato (fail-fast — 2.10). Tip generic (<string>,<number>) — qaytgan qiymat turini belgilaydi; validatsiyaPORTninumberga o'girsa ham, generic'ni to'g'ri berish sizning mas'uliyatingiz (yokiinfer: true— quyida).
2.3. Env validatsiya (Joi — eng muhim)
import * as Joi from "joi";
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({ // validatsiya
NODE_ENV: Joi.string().valid("development", "production", "test").default("development"),
PORT: Joi.number().default(3000),
DATABASE_URI: Joi.string().required(), // majburiy (yo'q bo'lsa — xato)
JWT_ACCESS_SECRET: Joi.string().min(32).required(), // kuchli (14)
JWT_REFRESH_SECRET: Joi.string().min(32).required(),
MAIL_HOST: Joi.string().required(),
}),
validationOptions: { abortEarly: false }, // barcha xatoni ko'rsat
});Env validatsiya (Joi — eng muhim):
validationSchema— har env o'zgaruvchini tekshiradi (tur, majburiy, format). Yo'q yoki noto'g'ri sozlama ilova ISHGA TUSHMAYDI (runtime xato emas — darrov ma'lum).required()(majburiy),min(32)(kuchli secret — 14),valid()(ruxsat etilgan).abortEarly: false(barcha xato). Bu — config'ning eng qimmatli xususiyati (xatoni erta ushlash).
2.3b. Env validatsiya — validate (class-validator — Joi'ga muqobil)
Joi o'rniga (yoki uni yoqtirmasangiz) NestJS validate funksiyasini beradi — bu 8.5'da o'rganilgan class-validator + class-transformer bilan ishlaydi (bir xil dekoratorlar — DTO'dagidek).
// env.validation.ts
import { plainToInstance } from "class-transformer";
import { IsEnum, IsNumber, IsString, MinLength, validateSync } from "class-validator";
enum Environment { // ruxsat etilgan muhitlar
Development = "development",
Production = "production",
Test = "test",
}
class EnvironmentVariables {
@IsEnum(Environment) // faqat enum qiymatlari
NODE_ENV: Environment;
@IsNumber()
PORT: number;
@IsString()
DATABASE_URI: string; // majburiy (@IsString undefined bo'lsa xato)
@IsString()
@MinLength(32) // kuchli secret (14)
JWT_ACCESS_SECRET: string;
@IsString()
@MinLength(32)
JWT_REFRESH_SECRET: string;
}
// Ishga tushishda chaqiriladigan funksiya
export function validate(config: Record<string, unknown>) {
const validated = plainToInstance(EnvironmentVariables, config, {
enableImplicitConversion: true, // "3000" (string) 3000 (number) avtomatik
});
const errors = validateSync(validated, { skipMissingProperties: false });
if (errors.length > 0) {
throw new Error(errors.toString()); // xato ilova ISHGA TUSHMAYDI (fail-fast)
}
return validated; // tekshirilgan (va o'girilgan) obyekt
}// app.module.ts — validationSchema o'rniga validate
ConfigModule.forRoot({
isGlobal: true,
validate, // funksiya (Joi schema emas)
});
validate(class-validator):validationSchema(Joi) o'rniga — funksiya (validate), u ishga tushishda barcha env'ni oladi. IchidaplainToInstance(class-transformer— 8.5) xom obyektni klass nusxasiga o'giradi, keyinvalidateSync(class-validator) dekoratorlar bo'yicha tekshiradi. Xato bo'lsathrowilova ishga tushmaydi (Joi'dagidek fail-fast).enableImplicitConversion: true— env string'larini ("3000") turga o'giradi (3000). Qaysi birini tanlash? Joi — soddaroq, mustaqil kutubxona;validate— agar loyihada allaqachonclass-validatorbo'lsa (DTO'lar — 8.5), bir xil uslub va dekoratorlar. Ikkalasi ham fail-fast beradi; ikkovidan birini tanlang (ikkalasini birga emas).
2.4. forRootAsync (config bog'liq sozlama)
// Boshqa modul config'dan foydalanadi (8.3, 8.9, 8.13)
TypeOrmModule.forRootAsync({
inject: [ConfigService], // config inject
useFactory: (config: ConfigService) => ({
type: "postgres",
url: config.get("DATABASE_URI"), // config'dan
autoLoadEntities: true,
}),
});
// JwtModule, MongooseModule, MailerModule — barchasi shu naqsh (config bilan)forRootAsync — modul sozlamasini config'dan olish (8.3: 2.4, 8.9: 2.3, 8.13: 2.1).
inject: [ConfigService]+useFactory. Bu naqsh butun kitobda takrorlandi (DB, JWT, Mail, Mongo). Config — markaz (barcha modul undan oladi). isGlobal bo'lsa, ConfigModule import shart emas.
2.5. Namespaced config (registerAs — modulli)
// config/database.config.ts
import { registerAs } from "@nestjs/config";
export default registerAs("database", () => ({ // "database" namespace
uri: process.env.DATABASE_URI,
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10) || 5432,
}));
// config/jwt.config.ts
export default registerAs("jwt", () => ({
accessSecret: process.env.JWT_ACCESS_SECRET,
accessExpires: process.env.JWT_ACCESS_EXPIRES || "15m",
refreshSecret: process.env.JWT_REFRESH_SECRET,
}));Namespaced config (
registerAs): sozlamalarni bo'limlarga ajratish (database,jwt,parseInt(env — string number). Bu — katta ilovada config'ni boshqarish (tartibli). 2.6'da yuklash, 2.7'da olish.
2.6. Namespace yuklash (load)
ConfigModule.forRoot({
isGlobal: true,
load: [databaseConfig, jwtConfig, mailConfig], // namespace'larni yuklash
validationSchema: Joi.object({ /* ... */ }),
});load — namespace config fayllarni yuklash (
load: [databaseConfig, ...]). Endi config "bo'limlar" bilan tashkillangan. Validatsiya 2.3-bob baribir env darajasida. Bu — 2.5 namespace'larni ulash.
2.7. Namespace olish (tur xavfsiz)
// 1-usul: get bilan (namespace.kalit)
const uri = this.config.get<string>("database.uri");
const secret = this.config.get("jwt.accessSecret");
// 2-usul: ConfigType (tur xavfsiz — afzal)
import { ConfigType } from "@nestjs/config";
@Injectable()
export class AuthService {
constructor(
@Inject(jwtConfig.KEY) // namespace inject
private jwt: ConfigType<typeof jwtConfig>, // TUR XAVFSIZ
) {}
example() {
const secret = this.jwt.accessSecret; // avtomatik tugatish (7)
}
}Namespace olish:
config.get("database.uri")(nuqta bilan), yokiConfigType(tur xavfsiz — afzal):@Inject(jwtConfig.KEY)+ConfigType<typeof jwtConfig>this.jwt.accessSecret(avtomatik tugatish, tur tekshiruvi — 7). Tur xavfsizlik — config'da xato kam (typo ushlanadi). Eng yaxshi amaliyot.
2.8. Turli muhit (dev/prod/test)
// Muhitga qarab env fayl
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV || "development"}`, // .env.development / .env.production
}); .env.development — lokal (DB localhost, debug log)
.env.production — server (real DB, kalit, info log)
.env.test — test (test DB)
.env.example — namuna (git'ga — qiymatsiz kalitlar ro'yxati)Turli muhit:
envFilePath: .env.${NODE_ENV}— muhitga qarab fayl (dev/prod/test)..env.example(git'ga — kalitlar ro'yxati, qiymatsiz — jamoa uchun). Production'da odatda env serverdan (Docker/CI — 10), fayl emas. NODE_ENV — markaziy o'zgaruvchi.
2.9. Xavfsizlik — sirlar (14)
.env GIT'GA TUSHMASIN (.gitignore — eng muhim! 14)
.env.example git'ga (qiymatsiz — namuna)
Production sirlar — secrets manager (Vault, AWS Secrets, env)
Kuchli secret (min 32 belgi — Joi tekshiradi — 2.3)
Har muhit — har xil secret (dev/prod alohida)
Kalit oshkor bo'lsa — DARROV almashtirish (rotatsiya)Sirlar xavfsizligi (14 — eng muhim):
.envgit'ga tushmasligi kerak (.gitignore— 4.5; oshkor bo'lsa — barcha kalit kompromis)..env.example(namuna — git'ga). Production — secrets manager (Vault/AWS — fayl emas). Kuchli secret (Joi min 32 — 2.3). Bu — kitobning eng muhim xavfsizlik qoidasi.
2.10. Config validatsiya foydasi (fail-fast)
Validatsiyasiz (yomon):
Ilova ishga tushadi 100 so'rovdan keyin "DATABASE_URI undefined" (runtime!)
Validatsiya bilan (yaxshi — fail-fast):
Ilova ishga tushmaydi "DATABASE_URI is required" (DARROV, deploy'da)
muammo production'ga yetib bormaydiFail-fast (2.3 foydasi): validatsiya bilan noto'g'ri config darrov (ishga tushishda) ma'lum — production'da 100 so'rovdan keyin emas. Bu — eng katta foyda (xato erta, deploy'da ushlanadi — 10). Konfiguratsiya xatosi — eng ko'p production muammosi; validatsiya buni oldini oladi.
2.11. Best practices (config)
ConfigModule isGlobal (bir marta — 2.1)
ConfigService (process.env emas — 2.2)
Joi validatsiya (fail-fast — 2.3, 2.10)
forRootAsync (modul sozlamasi config'dan — 2.4)
Namespace (registerAs — katta ilova — 2.5)
ConfigType (tur xavfsiz — 2.7)
Turli muhit (.env.NODE_ENV — 2.8)
.env gitignore; .env.example git'ga (14, 2.9)
Kuchli secret; secrets manager (prod — 2.9)3. Sintaksis — tez ma'lumotnoma
// Sozlash (2.1, 2.3)
ConfigModule.forRoot({ isGlobal: true, validationSchema: Joi.object({...}), load: [dbConfig] })
// Olish (2.2, 2.7)
this.config.get<string>("KEY") // undefined bo'lishi mumkin
this.config.getOrThrow<string>("KEY") // yo'q bo'lsa istisno (majburiy uchun)
this.config.get("KEY", "default") // default qiymat
this.config.get("namespace.kalit")
@Inject(dbConfig.KEY) private db: ConfigType<typeof dbConfig> // tur xavfsiz
// Validatsiya (2.3, 2.3b) — ikkovidan biri
validationSchema: Joi.object({ ... }) // Joi
validate: validateFunksiyasi // yoki class-validator
// Namespace 2.5-bob: registerAs("database", () => ({ uri: process.env.DATABASE_URI }))
// forRootAsync 2.4-bob: inject: [ConfigService], useFactory: (c) => ({ url: c.get("...") })4. Batafsil kod namunalari
Misol 1 — ConfigModule + Joi validatsiya (2.1, 2.3)
// app.module.ts
import * as Joi from "joi";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV || "development"}`,
cache: true,
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid("development", "production", "test").default("development"),
PORT: Joi.number().default(3000),
// Database
DATABASE_URI: Joi.string().required(),
// JWT (14 — kuchli)
JWT_ACCESS_SECRET: Joi.string().min(32).required(),
JWT_ACCESS_EXPIRES: Joi.string().default("15m"),
JWT_REFRESH_SECRET: Joi.string().min(32).required(),
JWT_REFRESH_EXPIRES: Joi.string().default("7d"),
// Mail
MAIL_HOST: Joi.string().required(),
MAIL_USER: Joi.string().email().required(),
MAIL_PASS: Joi.string().required(),
// Redis (8.15)
REDIS_HOST: Joi.string().default("localhost"),
REDIS_PORT: Joi.number().default(6379),
}),
validationOptions: { abortEarly: false }, // barcha xato (2.3)
}),
],
})
export class AppModule {}Misol 2 — Namespaced config fayllar (2.5)
// config/database.config.ts
import { registerAs } from "@nestjs/config";
export default registerAs("database", () => ({
uri: process.env.DATABASE_URI,
type: process.env.DB_TYPE || "postgres",
synchronize: process.env.NODE_ENV === "development", // prod'da false (6.13)
logging: process.env.NODE_ENV === "development",
}));
// config/jwt.config.ts
export default registerAs("jwt", () => ({
accessSecret: process.env.JWT_ACCESS_SECRET,
accessExpires: process.env.JWT_ACCESS_EXPIRES || "15m",
refreshSecret: process.env.JWT_REFRESH_SECRET,
refreshExpires: process.env.JWT_REFRESH_EXPIRES || "7d",
}));
// config/app.config.ts
export default registerAs("app", () => ({
port: parseInt(process.env.PORT, 10) || 3000,
env: process.env.NODE_ENV || "development",
clientUrl: process.env.CLIENT_URL || "http://localhost:5173",
}));Misol 3 — Namespace yuklash va validatsiya birga (2.6)
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [databaseConfig, jwtConfig, appConfig, mailConfig], // namespace
validationSchema: Joi.object({ /* Misol 1 */ }), // env validatsiya
validationOptions: { abortEarly: false },
}),
],
})
export class AppModule {}Misol 4 — forRootAsync (DB config bilan — 2.4)
// TypeORM 8.3-bob — config'dan
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: "postgres",
url: config.get("database.uri"), // namespace (2.7)
synchronize: config.get("database.synchronize"),
autoLoadEntities: true,
}),
}),
// MongoDB (8.13)
MongooseModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({ uri: config.get("database.uri") }),
}),
// JWT (8.9)
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get("jwt.accessSecret"),
signOptions: { expiresIn: config.get("jwt.accessExpires") },
}),
}),Misol 5 — ConfigType (tur xavfsiz — 2.7)
// auth.service.ts — namespace tur xavfsiz inject
import { ConfigType } from "@nestjs/config";
import jwtConfig from "../config/jwt.config";
@Injectable()
export class AuthService {
constructor(
@Inject(jwtConfig.KEY)
private jwtConf: ConfigType<typeof jwtConfig>, // TUR (avtomatik tugatish)
) {}
tokenSozlama() {
return {
secret: this.jwtConf.accessSecret, // tur tekshiriladi (typo xato)
expiresIn: this.jwtConf.accessExpires,
};
}
}Misol 6 — main.ts da config (2.2)
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = app.get(ConfigService); // app'dan olish
app.useGlobalPipes(new ValidationPipe({ whitelist: true })); // (8.5)
app.enableCors({ origin: config.get("app.clientUrl"), credentials: true }); // (5.20)
const port = config.get<number>("app.port");
await app.listen(port);
console.log(`Ilova ${port}-portda (${config.get("app.env")})`);
}
bootstrap();Misol 7 — .env fayllar (2.8, 2.9)
# .env.development (lokal — git'ga TUSHMAYDI)
NODE_ENV=development
PORT=3000
DATABASE_URI=postgresql://postgres:parol@localhost:5432/mana_dev
JWT_ACCESS_SECRET=dev_secret_kamida_32_belgi_bolishi_kerak_123
JWT_REFRESH_SECRET=dev_refresh_secret_kamida_32_belgi_456789
MAIL_HOST=smtp.mailtrap.io
REDIS_HOST=localhost
# .env.example (git'ga TUSHADI — namuna, qiymatsiz)
NODE_ENV=
PORT=
DATABASE_URI=
JWT_ACCESS_SECRET=
JWT_REFRESH_SECRET=
MAIL_HOST=
# .gitignore (14 — eng muhim)
.env
.env.*
!.env.exampleMisol 8 — Custom config service (validatsiya + tur — kengaytma)
// Tur xavfsiz config wrapper (ixtiyoriy — qattiq tur)
@Injectable()
export class AppConfigService {
constructor(private config: ConfigService) {}
get port(): number { return this.config.get<number>("app.port"); }
get databaseUri(): string { return this.config.get<string>("database.uri"); }
get jwtAccessSecret(): string { return this.config.get<string>("jwt.accessSecret"); }
get isProduction(): boolean { return this.config.get("app.env") === "production"; }
}
// Ishlatish: this.appConfig.databaseUri (to'liq tur xavfsiz, avtomatik tugatish)Bu AppConfigServiceni modulda provider sifatida ro'yxatga olib, boshqa joyga inject qilamiz (DI — 8.2):
// config.module.ts — o'z config modulingiz (ixtiyoriy — kengaytma)
import { Global, Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
@Global() // AppConfigService butun ilovaga
@Module({
imports: [ConfigModule], // ConfigService'dan foydalanadi
providers: [AppConfigService], // provider sifatida (8.2)
exports: [AppConfigService], // boshqa modul inject qilishi uchun
})
export class AppConfigModule {}
// Endi istalgan servisda:
@Injectable()
export class SomeService {
constructor(private appConfig: AppConfigService) {} // DI — tur xavfsiz
ish() {
if (this.appConfig.isProduction) { /* ... */ }
}
}ConfigService DI (provider):
ConfigServicening o'zi@nestjs/configberadigan provider'dir (isGlobal bo'lsa har joyda inject — 2.2). O'z wrapper'ingizni (AppConfigService) ham oddiy provider sifatida ro'yxatga olib,exportsbilan tashqariga chiqarasiz.main.tsda esa DI ishlamaydi (konstruktor yo'q) — u yerdaapp.get(ConfigService)(Misol 6) orqali qo'lda olamiz.
Misol 9 — Muhitga bog'liq xulq (2.8)
@Injectable()
export class LoggerService {
constructor(private config: ConfigService) {}
setup() {
const env = this.config.get("app.env");
if (env === "production") {
return { level: "info", format: "json" }; // prod (5.12)
}
return { level: "debug", format: "pretty" }; // dev (rangli)
}
}
// Swagger — faqat dev (8.8, xavfsizlik)
if (config.get("app.env") !== "production") {
SwaggerModule.setup("api", app, document);
}Misol 10 — Config testda (8.11)
// Test'da config mock (8.11)
const module = await Test.createTestingModule({
providers: [
AuthService,
{
provide: ConfigService,
useValue: { get: jest.fn((key) => ({ "jwt.accessSecret": "test_secret" }[key])) },
},
],
}).compile();
// Yoki test config moduli
ConfigModule.forRoot({ envFilePath: ".env.test", isGlobal: true })5. To'g'ri va noto'g'ri holatlar
1) process.env to'g'ridan o'qish
process.env.DATABASE_URI (validatsiyasiz, tarqoq — 2.2)
ConfigService.get (markazlashtirilgan, validatsiyalangan)2) Validatsiyasiz config
noto'g'ri config runtime xato (production'da — 2.10)
Joi validatsiya (fail-fast — ishga tushmaydi)3) .env git'da
.env commit (kalitlar oshkor — 14, falokat!)
.gitignore + .env.example4) Hardcode sozlama
const SECRET = "abc123" (kodda — 14)
env + config5) Bir secret hamma muhitda
dev/prod bir secret (dev sizsa, prod ham — 2.9)
har muhit alohida secret6. Keng tarqalgan xatolar va yechimlari
Xato 1 — ConfigService inject bo'lmaydi
Sababi: ConfigModule import yo'q (isGlobal emas — 2.1). Yechimi: isGlobal: true; yoki modulda import.
Xato 2 — Ilova ishga tushmaydi (validation error)
Sababi: env yo'q/noto'g'ri (2.3 — bu yaxshi! fail-fast). Yechimi: .env faylni to'ldiring; xato xabarini o'qing.
Xato 3 — config.get undefined
Sababi: kalit noto'g'ri, yoki namespace yuklanmagan 2.6-bob. Yechimi: kalitni aniqlang; load'ga qo'shing.
Xato 4 — Number string sifatida
Sababi: env har doim string 2.5-bob. Yechimi: parseInt yoki Joi.number (transform).
Xato 5 — Production'da dev config
Sababi: NODE_ENV noto'g'ri 2.8-bob. Yechimi: NODE_ENV=production; envFilePath.
Xato 6 — .env o'zgardi, lekin ta'sir yo'q
Sababi: cache yoki restart yo'q. Yechimi: ilovani qayta ishga tushiring; cache:true bo'lsa restart.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Env 5.8-bob: dotenv — NestJS @nestjs/config.
- DB (8.3, 8.13): forRootAsync config'dan.
- Auth 8.9-bob: JWT secret.
- Mail 8.10-bob: SMTP config.
- Bot 8.12-bob: BOT_TOKEN.
- Redis 8.15-bob: REDIS config.
- Xavfsizlik (14): sirlar, .gitignore.
- DevOps (10): muhit, secrets manager.
- Test 8.11-bob: config mock.
8. Eng yaxshi amaliyotlar (best practices)
- ConfigModule isGlobal (bir marta — 2.1).
- ConfigService (process.env emas — 2.2).
- Joi yoki
validate(validatsiya — fail-fast — 2.3, 2.3b, 2.10). getOrThrow(majburiy sozlama — undefined emas, istisno — 2.2).- forRootAsync (modul sozlamasi config'dan — 2.4).
- Namespace (registerAs — katta ilova — 2.5).
- ConfigType (tur xavfsiz — 2.7).
- Turli muhit (.env.NODE_ENV — 2.8).
- .env gitignore; .env.example git'ga (14, 2.9).
- Kuchli secret (min 32 — Joi); secrets manager (prod — 2.9).
- Muhitga bog'liq xulq (Swagger faqat dev — 2.8).
9. Amaliy loyiha: "Professional Config Tizimi"
NestJS config'ni mustahkamlash.
Maqsad
To'liq, validatsiyalangan, namespaced, tur xavfsiz config tizimi qurish.
Talablar (requirements)
- ConfigModule: isGlobal, cache, muhit fayl (Misol 1, 2.1, 2.8).
- Joi validatsiya: barcha env (required, min, valid — Misol 1, 2.3).
- Namespace: database, jwt, app, mail config (Misol 2, 2.5).
- Load: namespace'larni yuklash (Misol 3, 2.6).
- forRootAsync: DB/JWT/Mail config'dan (Misol 4, 2.4).
- ConfigType: tur xavfsiz inject (Misol 5, 2.7).
- main.ts: port/cors config'dan (Misol 6, 2.2).
- .env fayllar: dev/prod/example + .gitignore (Misol 7, 2.9).
- Muhitga bog'liq: Swagger faqat dev, log darajasi (Misol 9, 2.8).
- Xavfsizlik: kuchli secret, .env gitignore (2.9, 14).
Maslahatlar (hint)
- isGlobal (qayta import emas — 2.1, 1-xato).
- Joi required/min (fail-fast — 2.3, 2-xato).
- registerAs namespace 2.5-bob.
- ConfigType tur xavfsiz 2.7-bob.
- .env gitignore + example (14, 3-holat).
- env string parseInt/Joi.number (4-xato).
"Tayyor" mezonlari (acceptance criteria)
- ConfigModule (isGlobal, muhit).
- Joi validatsiya (barcha env).
- Namespace (4 config).
- Load.
- forRootAsync (DB/JWT/Mail).
- ConfigType (tur xavfsiz).
- main.ts config.
- .env fayllar + gitignore.
- Muhitga bog'liq xulq.
- Xavfsizlik (secret, gitignore).
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda NestJS config tizimini chuqur o'rgandik:
- ConfigModule (isGlobal — 2.1); ConfigService (DI — 2.2); Joi validatsiya (fail-fast — eng muhim — 2.3, 2.10).
- forRootAsync (modul config'dan — 2.4); namespace (registerAs — 2.5, 2.6); ConfigType (tur xavfsiz — 2.7).
- Turli muhit (.env.NODE_ENV — 2.8); xavfsizlik (sirlar, .gitignore, secrets manager — 14 — 2.9).
Keyingi bob — 8.15-bob: Caching (Redis), Logger, Error handling. Config'ni bildik; endi ishlash va kuzatuvni — caching (CacheModule + Redis — 5.21 — tezlik), logger (Pino/Winston — 5.12 — kuzatuv), error handling (global exception filter — 8.6) — chuqur ko'ramiz. Bu — production ilovaning tezligi, kuzatuvi, barqarorligi.
Foydalanilgan rasmiy manbalar
- docs.nestjs.com/techniques/configuration (ConfigModule, validationSchema, registerAs, ConfigType)
- 12factor.net (config — env'da sirlar)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!