Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. Provider nima
- 2.1 Ta'rif
- 2.2 @Injectable()
- 2.3 providers ro'yxati
- 3. Constructor orqali bog'liqlik
- 3.1 private readonly — qisqa yozuv
- 3.2 Klass turi — tokenning o'zi
- 3.3 Interfeys — @Inject(TOKEN)
- 3.4 @Optional() — bo'lmasa ham bo'ladi
- 4. Singleton: bitta nusxa
- 4.1 Haqiqiy tajriba
- 4.2 Nega bu muhim
- 5. JS modelida @Injectable yorlig'i
- 6. Xatolar: provider yo'q va yorliq yo'q
- 6.1 providers da yo'q
- 6.2 @Injectable() unutilgan
- 7. Xizmat va yordamchi funksiya
- 8. Fabrika funksiyadan klassga
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): fabrikadan klassga
- 3-mashq (qiyin): modelga @Optional qo'shing
- 11. Loyiha qadami: xizmatlar
- 11.1 Qaysi fayllar
- 11.2 vazifalar.service.ts
- 11.3 eksport.service.ts
- 11.4 bogliqliklar.module.ts
- 11.5 Modullar
- 11.6 Tekshiruv: bitta kutilgan xato
- 12. Real ishda
- Xulosa
- Manbalar
NestJS provider va xizmatlari: @Injectable, constructor injection va singleton
Qisqacha: Provider — Nest o'zi yaratib, boshqa klasslarga bera oladigan obyekt. Eng ko'p uchraydigan turi — xizmat (service): biznes qoidalari yashaydigan
@Injectable()klass. Uni modulningprovidersro'yxatiga yozasiz, keyin controller constructor'ida so'raysiz:constructor(private readonly xizmat: VazifalarService). Nest nusxani o'zi yaratadi va sukut bo'yicha butun ilovada bitta nusxa ishlatadi (singleton). Interfeys turidagi bog'liqlik uchun token kerak:@Inject(VAZIFA_OMBORI).
Bu darsda
@Injectable()nima qilishini va u unutilsa nima bo'lishini haqiqiy xato bilan ko'rasiz.- Constructor orqali bog'liqlik olishni (
private readonly,@Inject,@Optional) yoza olasiz. - Provider sukut bo'yicha singleton ekanini o'lchab ko'rasiz va bu nega muhimligini tushuntirasiz.
- Qachon klass-xizmat, qachon oddiy yordamchi funksiya kerakligini farqlaysiz.
vazifalar-apining 25-qismdagivazifaXizmatifabrikasiniVazifalarServiceklassiga ko'chirasiz.
Oldin bilishingiz kerak: Controller'lar va route dekoratorlari, Modullar, express.Router va qatlamli loyiha tuzilmasi, Klasslar TypeScript'da, Legacy dekoratorlar va reflect-metadata.
O'tgan darsda controller'lar yozildi, lekin npm run tip to'rtta xato berdi: VazifalarService va EksportService hali yo'q edi. Bu darsda ularni yozamiz. Controller'lar HTTP bilan ishlaydi, xizmatlar esa "nima qilish kerak" degan savolga javob beradi.
1. Nega bu kerak?
25-qismda xizmat fabrika funksiya edi. ilova.ts uni qo'lda yaratib, routerga berardi:
const xizmat = vazifaXizmati(ombor, () => joriyLog(log), xabarchi);
v1.use("/vazifalar", vazifalarRouteri(xizmat, { tz }));
v1.use("/teglar", teglarRouteri(xizmat));Bu kodda ikki narsa yaxshi: xizmat HTTP'ni bilmaydi va omborni tashqaridan oladi. Ikkita narsa esa qo'lda: kim kimni yaratishi va yaratish tartibi. Ombor xizmatdan oldin, xizmat routerdan oldin bo'lishi kerak. Bitta router ikkita xizmatni so'rasa, ilova.ts ga yana bir qator qo'shasiz.
Uch xizmatda bu muammo emas. O'ttizta xizmatda esa ilova.ts "kim kimga kerak" degan ulkan ro'yxatga aylanadi. Har yangi bog'liqlik bu ro'yxatni o'zgartiradi.
Nest bu ishni o'z zimmasiga oladi. Siz xizmatga yorliq yopishtirasiz va constructor'da "menga nima kerak"ligini yozasiz. Qolganini — kimni qachon yaratish, kimga berish — Nest qiladi.
O'xshatish: oshxonadagi ta'minot bo'limi. Oshpaz (controller) "menga go'sht va guruch kerak" deb buyurtma yozadi. Ta'minotchi (Nest) omborni biladi, mahsulotni olib keladi va hamma oshpazga bitta ombordan beradi. Oshpaz bozorga o'zi bormaydi.
2. Provider nima
2.1 Ta'rif
Provider (ta'minlovchi) — Nest konteyneri yarata oladigan va boshqa klassga bera oladigan har qanday narsa. Bu so'z bilan NestJS nima va nega darsida tanishgansiz. Provider turlari:
| Tur | Misol | Qaysi darsda |
|---|---|---|
Klass (@Injectable) |
VazifalarService |
shu dars |
Tayyor qiymat (useValue) |
log, sozlamalar | shu dars, Custom provider'lar |
Fabrika (useFactory) |
baza ulanishi | Custom provider'lar |
Taxallus (useExisting) |
bitta obyektga ikki nom | Custom provider'lar |
Bu darsda asosan birinchi tur — klass. U eng ko'p uchraydi. Ikkinchisini loyihada ham ishlatamiz, chunki vazifalar-api da ombor va log Nest'dan oldin yaratiladi.
2.2 @Injectable()
@Injectable()
export class VazifalarService {
constructor(
@Inject(VAZIFA_OMBORI) private readonly ombor: VazifaOmbori,
) {}
}@Injectable() — "bu klassni Nest yarata oladi, uning constructor'iga qara" degan yorliq. Uning eng muhim ishi ko'rinmas: TypeScript emitDecoratorMetadata sozlamasi bilan dekoratorli klassning constructor turlarini JavaScript'ga yozib qo'yadi (design:paramtypes). Dekorator yo'q klass uchun bu yozuv paydo bo'lmaydi. Buni legacy dekoratorlar darsida va NestJS nima va nega darsida __metadata("design:paramtypes", [AppService]) qatori bilan ko'rgansiz.
2.3 providers ro'yxati
Yorliqning o'zi yetmaydi. Klass modulning providers iga yozilishi kerak:
@Module({
controllers: [VazifalarController, TeglarController],
providers: [VazifalarService],
exports: [VazifalarService],
})
export class VazifalarModule {}providers — "shu modul nimani yaratadi". exports — "tashqariga nimani beradi" (Modullar darsidagi qoida). Ikkala controller bitta modulda — ular xizmatni import qilmasdan oladi.
Tekshirib ko'ring:
@Injectable()bor, lekin klass hech bir modulningprovidersida yo'q. Controller uni constructor'da so'rasa nima bo'ladi?
Javob
Ilova ishga tushmaydi: Nest can't resolve dependencies of the …Controller (?). Yorliq "yarata olaman" deydi, providers esa "yarat" deydi. Ikkinchisi bo'lmasa, konteyner bu klassni bilmaydi. Haqiqiy chiqishni «Xatolar: provider yo'q va yorliq yo'q» bo'limida ko'ramiz.
3. Constructor orqali bog'liqlik
3.1 private readonly — qisqa yozuv
constructor(private readonly xizmat: VazifalarService) {}Bu — TypeScript'ning parameter property yozuvi (Klasslar TypeScript'da). U ikki ishni bir qatorda qiladi: parametrni oladi va this.xizmat maydoniga yozadi. Uzun shakli:
readonly #xizmat: VazifalarService;
constructor(xizmat: VazifalarService) {
this.#xizmat = xizmat;
}Nest kodida deyarli har doim qisqa shakl yoziladi. private — maydon klass tashqarisidan ko'rinmasin. readonly — konstruktordan keyin uni almashtirib bo'lmasin. Bog'liqlik bir marta beriladi va o'zgarmaydi — readonly shu niyatni kodda ko'rsatadi.
3.2 Klass turi — tokenning o'zi
private readonly xizmat: VazifalarService da VazifalarService ikki rol o'ynaydi. TypeScript uchun — tur. Nest uchun — token: "konteynerdan shu kalit bilan narsani ber". Klass ish vaqtida ham mavjud (u JavaScript funksiyasi), shuning uchun metama'lumotga yoziladi va Nest uni o'qiy oladi.
3.3 Interfeys — @Inject(TOKEN)
VazifaOmbori — interfeys. Ish vaqtida u yo'q: TypeScript uni o'chirib tashlaydi. Metama'lumotda o'rniga Object qoladi, Nest esa Object dan nima yaratishni bilmaydi. Shuning uchun token qo'lda beriladi:
constructor(
@Inject(VAZIFA_OMBORI) private readonly ombor: VazifaOmbori,
@Inject(LOG) private readonly log: Log,
) {}VAZIFA_OMBORI — Symbol, o'tgan darsda yozilgan tokenlar.ts da. @Inject(VAZIFA_OMBORI) — "bu parametr uchun shu kalitga qara". Tur (: VazifaOmbori) esa faqat TypeScript uchun qoladi — kodni tekshirish va avtomatik to'ldirish uchun. Interfeys tokensiz ishlatilsa Nest qanday chalg'ituvchi xato berishini DI va IoC konteyner darsida haqiqiy matni bilan ko'ramiz.
3.4 @Optional() — bo'lmasa ham bo'ladi
vazifalar-api da webhook xabarchisi ixtiyoriy: WEBHOOK_URL berilmasa, u yo'q. 25-qismda bu xabarchi?: Xabarchi parametri edi. Nest'da:
@Optional() @Inject(XABARCHI)
private readonly xabarchi: Xabarchi | null = null,@Optional() — "bu provider topilmasa, xato berma, undefined ber". Kanon kodida biroz boshqacha yo'l ham bor: provider har doim ro'yxatda, lekin qiymati null bo'lishi mumkin (useValue: xabarchi ?? null). Shunda xizmat ichida tekshiruv bitta: this.xabarchi !== null. @Optional() esa token umuman ro'yxatdan tushib qolsa ham ilovani yiqitmaydi — ikki qavatli himoya.
Tekshirib ko'ring: Nega
@Inject(LOG) private readonly log: Logda@Injectkerak,private readonly xizmat: VazifalarServiceda esa kerak emas?
Javob
VazifalarService — klass, u ish vaqtida mavjud va metama'lumotga o'zi yoziladi. Log esa TypeScript turi (pino logger turi) — ish vaqtida yo'q, metama'lumotda Object qoladi. Nest Object bo'yicha hech narsa topa olmaydi, shuning uchun aniq token — LOG — kerak.
4. Singleton: bitta nusxa
4.1 Haqiqiy tajriba
Nest provider'ni sukut bo'yicha butun ilova uchun bir marta yaratadi. Buni o'z demo loyihamizda tekshirdik (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0). Hisoblagich xizmati har chaqiruvda sonni oshiradi, ikki controller uni ishlatadi:
@Injectable()
class HisoblagichService {
#soni = 0;
constructor() {
yaratildi++;
}
oshir(): number {
return ++this.#soni;
}
}
@Controller('a')
class AController {
constructor(private readonly h: HisoblagichService) {}
@Get() ol() {
return { a: this.h.oshir() };
}
}
// BController — xuddi shunday, { b: … } qaytaradiTo'rtta so'rov navbat bilan /a va /b ga:
1) singleton: OK
GET /a → 200 {"a":1}
GET /b → 200 {"b":2}
GET /a → 200 {"a":3}
GET /b → 200 {"b":4}
HisoblagichService yaratildi: 1 martaSonlar 1, 2, 3, 4 — uzilmasdan o'sdi. Ikki controller bitta hisoblagichni ishlatdi. Konstruktor bir marta chaqirildi — ilova ishga tushganda, birinchi so'rovdan oldin.
flowchart LR
A["AController"] --> H["HisoblagichService (1 nusxa)"]
B["BController"] --> H
H --> S["#soni: 1, 2, 3, 4"]Diagrammadagi ikki strelka bitta qutiga keladi — bu singleton. NestJS nima va nega darsidagi JS konteyner ham aynan shunday ishlagandi.
4.2 Nega bu muhim
EksportService ichida eksport ishlari Map da saqlanadi:
@Injectable()
export class EksportService {
readonly #ishlar = new Map<string, Ish>();
// …
}POST /v1/eksportlar ishni Map ga yozadi, GET /v1/eksportlar/:id uni o'qiydi. Ikkalasi bitta nusxadan foydalangani uchun ishlaydi. Agar har so'rov yangi EksportService olsa, GET bo'sh Map ni ko'rardi va "topilmadi" derdi.
Singleton'ning ikkinchi tomoni: nusxa hamma so'rovlar uchun umumiy. Unga so'rovga xos ma'lumot (joriy foydalanuvchi, sorovId) yozmang — ikki parallel so'rov bir-birining qiymatini ustidan yozadi. vazifalar-api da so'rov konteksti AsyncLocalStorage orqali olinadi: joriyLog(this.log) har chaqiruvda joriy so'rovning logger'ini beradi. Har so'rovga alohida nusxa ham mumkin — Provider scope'lari darsida, narxi bilan.
Diqqat: Singleton xizmatda
this.joriyFoydalanuvchi = …kabi maydon — klassik xato. Bitta so'rovda yozilgan qiymatni boshqa so'rov o'qiydi. Bu xato testda deyarli ko'rinmaydi (testda so'rovlar ketma-ket), prodakshnda esa yuk ostida chiqadi.
5. JS modelida @Injectable yorlig'i
Nest ichida @Injectable nima qilishini kichik modelda takrorlaymiz. Bu blok brauzerda ishlaydi. Dekorator o'rniga oddiy funksiya: injectable(Klass, kerak) metama'lumot jadvaliga "bu klassga nima kerak" deb yozadi. Konteyner faqat shu jadvalga qaraydi:
const metama = new Map(); // klass → kerakli klasslar
function injectable(Klass, kerak) {
metama.set(Klass, kerak);
}
function yarat(Klass, tayyor = new Map()) {
if (tayyor.has(Klass)) return tayyor.get(Klass);
// yorliq yo'q — "bog'liqligi yo'q" deb hisoblanadi
const kerak = metama.get(Klass) ?? [];
const nusxa = new Klass(...kerak.map((K) => yarat(K, tayyor)));
tayyor.set(Klass, nusxa);
return nusxa;
}
class SoatXizmati {
hozir() { return "2026-11-02T09:00:00Z"; }
}
class HisobotXizmati {
constructor(soat) { this.soat = soat; }
sarlavha() { return `Hisobot (${this.soat.hozir()})`; }
}
injectable(SoatXizmati, []);
injectable(HisobotXizmati, [SoatXizmati]);
console.log(yarat(HisobotXizmati).sarlavha());
metama.delete(HisobotXizmati); // yorliqni "unutdik"
try {
console.log(yarat(HisobotXizmati).sarlavha());
} catch (e) {
console.log(`${e.name}: ${e.message}`);
}Konsolda:
Hisobot (2026-11-02T09:00:00Z)
TypeError: Cannot read properties of undefined (reading 'hozir')Ikkinchi qatorga qarang. Yorliq yo'q bo'lganda konteyner xato bermadi — klassni argumentsiz yaratdi. this.soat — undefined, xato esa faqat metod chaqirilganda chiqdi. Haqiqiy Nest ham aynan shunday qiladi — keyingi bo'limda ko'ramiz.
Model haqiqiy Nest'dan soddaroq: unda modul chegarasi, tokenlar va @Optional yo'q. Haqiqiy Nest metama'lumotni Reflect.getMetadata("design:paramtypes", Klass) bilan o'qiydi — bizning metama.get(Klass) ning o'rni.
6. Xatolar: provider yo'q va yorliq yo'q
6.1 providers da yo'q
Demo loyihada AController ni hisoblagichsiz modulga qo'ydik:
@Module({ controllers: [AController] })
class ProvidersizModule {}Nest can't resolve dependencies of the AController (?). Please make sure that the argument HisoblagichService at index [0] is available in the ProvidersizModule module.
Potential solutions:
- Is ProvidersizModule a valid NestJS module?
- If HisoblagichService is a provider, is it part of the current ProvidersizModule?
- If HisoblagichService is exported from a separate @Module, is that module imported within ProvidersizModule?
@Module({
imports: [ /* the Module containing HisoblagichService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errorsTarjimasi: "Nest AController ning bog'liqliklarini topa olmadi (?). ProvidersizModule modulida 0-indeksdagi HisoblagichService argumenti mavjudligiga ishonch hosil qiling." Xabarni Modullar darsidagidek o'qing: (?) — topilmagan argument, index [0] — constructor'dagi o'rni, in the ProvidersizModule module — qayerda qidirilgani. Ikkinchi maslahat aynan bizning holat: "HisoblagichService provider bo'lsa, u joriy modulning qismimi?" Yo'q — providers: [HisoblagichService] qo'shilsa, xato yo'qoladi.
Bu xato yaxshi xato: ilova ishga tushayotganda chiqadi va birinchi so'rovgacha yetmaydi.
6.2 @Injectable() unutilgan
Endi xavfliroq holat. DekoratorsizService ga SoatService kerak, lekin @Injectable() yozilmagan:
// @Injectable() yo'q — lekin constructor'da bog'liqlik bor
class DekoratorsizService {
constructor(private readonly soat: SoatService) {}
sarlavha(): string {
return `Hisobot (${this.soat.hozir()})`;
}
}
@Module({
controllers: [CController],
providers: [SoatService, DekoratorsizService],
})
class DekoratorsizModule {}Natija (logda jarayon raqami va vaqtni olib tashladik, iz qatorlari qisqartirildi):
3) @Injectable() unutilgan: OK
ERROR [ExceptionsHandler] TypeError: Cannot read properties of undefined (reading 'hozir')
at DekoratorsizService.sarlavha (file:///…/dist/t-xizmat.js:78:38)
at CController.ol (file:///…/dist/t-xizmat.js:87:28)
GET /c → 500 {"statusCode":500,"message":"Internal server error"}Ilova ishga tushdi (OK). Klass providers da bor, Nest uni yaratdi — lekin metama'lumot bo'lmagani uchun argumentsiz. Xato faqat so'rov kelganda chiqdi: this.soat — undefined. Tarjimasi: "undefined ning xususiyatlarini o'qib bo'lmaydi (hozir o'qilmoqda)". Xabar SoatService haqida bir og'iz ham demaydi — siz undefined qayerdan kelganini o'zingiz qidirasiz. JS modelidagi ikkinchi qator aynan shu edi.
Diqqat: Constructor'ida bog'liqligi bor har provider klassiga
@Injectable()yozing. Bog'liqligi yo'q klass yorliqsiz ham ishlaydi — shuning uchun xato kech, birinchi bog'liqlik qo'shilganda chiqadi.
7. Xizmat va yordamchi funksiya
Har bir funksiyani provider qilish shart emas. vazifalar-api da ikkalasi ham bor:
| Xizmat (provider) | Yordamchi (oddiy funksiya) |
|---|---|
VazifalarService |
asosYol(req, toplam) |
EksportService |
csvQator(qiymatlar, ajratgich) |
ombor (VAZIFA_OMBORI) |
kursorYoz, proyeksiya |
Farqni qanday topish mumkin? Uchta savol:
- Holat bormi?
EksportServiceda#ishlarMap'i — holat. Holatli narsa provider bo'ladi: kim uni yaratishi va nechta nusxa bo'lishi muhim. - Tashqi bog'liqlik bormi? Ombor, log, tarmoq — testda ularni almashtirish kerak bo'ladi. Provider almashtiriladi, oddiy import qilingan funksiya — yo'q.
- Natija faqat argumentlarga bog'liqmi?
asosYol— kirish satri, chiqish satri. Unda ombor ham, log ham yo'q. Bunday toza funksiya oddiyexport functionbo'lib qoladi va to'g'ridan import qilinadi.
Uchala savolga "yo'q" bo'lsa — yordamchi funksiya. Uni provider qilsangiz, faqat dekorator va modul yozuvi qo'shiladi, foyda yo'q. O'tgan darsda asosYol brauzerda sinalgan edi — u Nest'ni umuman bilmaydi.
Tekshirib ko'ring:
narxniFormatla(tiyin)funksiyasiIntl.NumberFormat("uz-Latn-UZ")bilan son qaytaradi. Uni provider qilish kerakmi?
Javob
Kerak emas. Holati yo'q, tashqi bog'liqligi yo'q, natija faqat argumentga bog'liq — toza funksiya. Uni testda almashtirishga ehtiyoj ham yo'q: haqiqiy funksiya tez va deterministik. Agar u valyuta kursini tarmoqdan olsa edi — boshqa gap: tarmoq bog'liqligi paydo bo'ladi va xizmat qilish mantiqli.
8. Fabrika funksiyadan klassga
Ko'chishning asosiy qismi — 25-qismdagi xizmat.ts. Mantiq aynan qoladi, faqat shakl o'zgaradi:
25–27 (xizmat.ts) |
Nest (vazifalar.service.ts) |
|---|---|
export function vazifaXizmati(ombor, log, xabarchi) |
@Injectable() export class VazifalarService |
| parametrlar | constructor(@Inject(...) private readonly ...) |
ombor.ol(id) |
this.ombor.ol(id) |
log().info(...) |
joriyLog(this.log).info(...) |
xabarchi?: Xabarchi, !== undefined |
@Optional(), Xabarchi | null, !== null |
return { royxat, ol, yarat, … } |
klass metodlari |
ilova.ts da vazifaXizmati(…) |
providers: [VazifalarService] |
Ikki kichik farq bor. Birinchisi — log. 25-qismda xizmat log ni funksiya sifatida olardi (() => joriyLog(log)), har chaqiruvda joriy so'rov logger'ini olish uchun. Endi xizmat ildiz logger'ni oladi va joriyLog(this.log) ni o'zi chaqiradi — natija bir xil. Ikkinchisi — xabarchi bo'lmasa undefined emas, null. Sababi «@Optional() — bo'lmasa ham bo'ladi» bo'limida.
Qolgan hamma narsa — MAKS_VAZIFA = 1000, 404 va 412 xatolari, faqat false → true o'tishida webhook — so'zma-so'z ko'chdi. Shuning uchun 25–27 dagi testlar xizmatning xulqini o'zgarishsiz tekshira oladi.
flowchart TB
VC["VazifalarController"] --> VS["VazifalarService"]
TC["TeglarController"] --> VS
EC["EksportController"] --> ES["EksportService"]
VS -->|"@Inject(VAZIFA_OMBORI)"| O["ombor"]
VS -->|"@Inject(LOG)"| L["log"]
VS -.->|"@Optional XABARCHI"| X["xabarchi yoki null"]
ES --> O
ES --> LDiagrammada tokenli bog'liqliklar strelka yorlig'ida. Punktir — ixtiyoriy bog'liqlik. Ombor va log ikki xizmatda bir xil — ular ham singleton.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Nest can't resolve dependencies of the X (?) |
klass providers da yo'q yoki eksport qilinmagan |
providers ga qo'shing yoki egasidan exports |
Ilova ko'tariladi, so'rovda Cannot read properties of undefined |
@Injectable() unutilgan |
har bog'liqlikli provider'ga yorliq |
… the argument at index [0] … (nomsiz) |
interfeys turi tokensiz | @Inject(TOKEN) |
| Ikki so'rov bir-birining ma'lumotini ko'radi | singleton'da so'rovga xos maydon | AsyncLocalStorage yoki parametr |
| Testda soxta ombor ishlamaydi | xizmat new PgOmbori() ni o'zi yaratadi |
bog'liqlikni constructor'dan oling |
Oxirgi qator 25-qismdagi qoidaning davomi: obyekt o'ziga kerak narsani o'zi new bilan yaratsa, uni tashqaridan almashtirib bo'lmaydi. Nest bu qoidani majburiy qilmaydi — new yozish taqiqlanmagan — lekin Nest'ning test vositalari (keyinroq NestJS unit testlari) constructor'dan kelgan bog'liqliklarni almashtirishga qurilgan.
10. Mashqlar
Mashqlar kurs/mashqlar/30/05-provider-xizmat/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- Klassni Nest yarata oladigan qiluvchi dekorator —
@() - Interfeys turidagi bog'liqlik uchun parametrga yoziladigan dekorator —
@(TOKEN) - Topilmasa xato bermaydigan bog'liqlik —
@() - Provider sukut bo'yicha butun ilovada nechta nusxada? —
asosYol(req, "vazifalar")provider bo'lishi kerakmi (ha/yo'q)? —
Yechim
- Injectable — metama'lumot yozilishi uchun yorliq.
- Inject —
@Inject(VAZIFA_OMBORI); interfeys ish vaqtida yo'q. - Optional — topilmasa
undefinedberiladi. - 1 — singleton; demo tajribada
yaratildi: 1 marta. - yo'q — toza funksiya: holat ham, tashqi bog'liqlik ham yo'q.
2-mashq (o'rta): fabrikadan klassga
25-qism uslubidagi fabrika funksiya berilgan:
function narxXizmati(kurs) {
return {
somda: (usd) => Math.round(usd * kurs.ol()),
};
}Uni NarxXizmati klassiga aylantiring: bog'liqlik constructor'dan kelsin, somda(usd) metod bo'lsin. Soxta kurs obyekti ({ ol: () => 12_650 }) bilan somda(10) va somda(2.5) ni chiqaring. Keyin boshqa soxta kurs (12_700) bilan yangi nusxa yarating — natija o'zgarishi kerak. Ishora: klass sintaksisi, this.kurs.
Yechim
class NarxXizmati {
constructor(kurs) {
this.kurs = kurs;
}
somda(usd) {
return Math.round(usd * this.kurs.ol());
}
}
const soxtaKurs = { ol: () => 12_650 };
const x = new NarxXizmati(soxtaKurs);
console.log(x.somda(10));
console.log(x.somda(2.5));
const boshqa = new NarxXizmati({ ol: () => 12_700 });
console.log(boshqa.somda(10));Konsolda:
126500
31625
127000Mantiq aynan bir xil, faqat bog'liqlik endi this.kurs da. Nest'da kurs oldiga @Inject(KURS) qo'yilardi (kurs manbai interfeys bo'lgani uchun) va klassga @Injectable(). Testda esa xuddi shu yerdagidek soxta kurs beriladi — kod o'zgarmaydi.
3-mashq (qiyin): modelga @Optional qo'shing
Darsdagi JS modelida kerak ro'yxatiga obyekt ham yozilishi mumkin bo'lsin: { klass: Xabarchi, ixtiyoriy: true }. Bunday bog'liqlik ro'yxatda (metama da) bo'lmasa, konteyner xato bermasin — undefined bersin. Oddiy (ixtiyoriy bo'lmagan) bog'liqlik ro'yxatda bo'lmasa esa Error tashlasin: "<Klass> ning <i>-argumenti (<Bog'liqlik>) ro'yxatda yo'q". Sinov: Xizmat ga Ombor (majburiy) va Xabarchi (ixtiyoriy) kerak. Ishora: kerak.map((k, i) => …), typeof k === "function" — oddiy klass, aks holda obyekt.
Yechim
const metama = new Map();
function injectable(Klass, kerak) {
metama.set(Klass, kerak);
}
function yarat(Klass, tayyor = new Map()) {
if (tayyor.has(Klass)) return tayyor.get(Klass);
const kerak = metama.get(Klass) ?? [];
const argumentlar = kerak.map((k, i) => {
const B = typeof k === "function" ? k : k.klass;
const ixtiyoriy = typeof k === "function" ? false : k.ixtiyoriy;
if (!metama.has(B)) {
if (ixtiyoriy) return undefined;
throw new Error(
`${Klass.name} ning ${i}-argumenti (${B.name}) ` +
"ro'yxatda yo'q",
);
}
return yarat(B, tayyor);
});
const nusxa = new Klass(...argumentlar);
tayyor.set(Klass, nusxa);
return nusxa;
}
class Ombor {}
class Xabarchi {}
class Xizmat {
constructor(ombor, xabarchi) {
this.ombor = ombor;
this.xabarchi = xabarchi;
}
}
injectable(Xizmat, [Ombor, { klass: Xabarchi, ixtiyoriy: true }]);
try {
yarat(Xizmat);
} catch (e) {
console.log(e.message);
}
injectable(Ombor, []);
const x = yarat(Xizmat);
console.log(x.ombor instanceof Ombor, x.xabarchi);
injectable(Xabarchi, []);
console.log(yarat(Xizmat).xabarchi instanceof Xabarchi);Konsolda:
Xizmat ning 0-argumenti (Ombor) ro'yxatda yo'q
true undefined
trueBirinchi urinish — Ombor majburiy va ro'yxatda yo'q, xato. Ikkinchisida Ombor bor, Xabarchi yo'q — undefined, xato yo'q. Uchinchisi yangi tayyor jadvali bilan boshlandi va Xabarchi ni ham topdi. Nest'ning @Optional() i ham shunday: provider bo'lmasa — undefined. Kanon xizmatidagi = null sukut qiymati shu undefined ni null ga aylantiradi.
11. Loyiha qadami: xizmatlar
11.1 Qaysi fayllar
Bu dars — butun v1 API'ni Nest'ga ko'chirishning uchinchi qismi. Yangi fayllar:
| Fayl | Nima |
|---|---|
src/vazifalar/vazifalar.service.ts |
25-qism xizmat.ts mantig'i, @Injectable klass |
src/eksport/eksport.service.ts |
eksport.ts dagi ishlar va fon bajarilishi |
src/bogliqliklar.module.ts |
ombor, log, xabarchi, sozlamalar — useValue |
O'zgaradigan: vazifalar.module.ts, eksport.module.ts — providers va exports. Eski xizmat.ts, eksport.ts hali joyida — Express API ular bilan ishlashda davom etadi. Kanon fayllarida qatorlar uzun edi — telefon uchun bo'ldik (izohlar qisqartirildi, bitta joyda takror ifoda const joriy ga olindi; ma'no aynan). Bo'lingan nusxani biz kanon nusxasida tsc --noEmit va ESLint bilan tekshirdik.
11.2 vazifalar.service.ts
// vazifalar.service.ts — biznes qoidalari (25-qism xizmat.ts dan;
// mantiq o'zgarmadi). Endi factory funksiya emas — @Injectable klass:
// bog'liqliklarni Nest konteyneri constructor orqali beradi (ombor —
// token bilan, chunki VazifaOmbori — interfeys)
import { Inject, Injectable, Optional } from "@nestjs/common";
import {
KonfliktXatosi, ShartXatosi, TopilmadiXatosi,
} from "../umumiy/xatolar.ts";
import { joriyLog } from "../umumiy/log.ts";
import type { Log } from "../umumiy/log.ts";
import type { Xabarchi } from "../umumiy/webhook.ts";
import { LOG, VAZIFA_OMBORI, XABARCHI } from "../tokenlar.ts";
import type {
AlmashtirishVazifasi, RoyxatNatija, RoyxatSorovi, Shart, TegSoni,
VazifaOmbori, YozishNatijasi,
} from "./ombor.ts";
import type {
ToliqVazifa, Vazifa, VazifaOzgarishi, YangiVazifa,
} from "./vazifa.ts";
export const MAKS_VAZIFA = 1000;
const topilmadi = (id: number) => new TopilmadiXatosi(
`Vazifa ${String(id)} topilmadi`, "vazifa-topilmadi",
"Vazifa topilmadi",
);
/** Ombor natijasini qiymatga yoki xatoga aylantiradi:
* topilmadi → 404, versiya mos emas → 412 */
function natija<T>(id: number, n: YozishNatijasi<T>): T {
if (n.tur === "topilmadi") throw topilmadi(id);
if (n.tur === "konflikt") {
const joriy = String(n.joriy.versiya);
throw new ShartXatosi(
`Vazifa ${String(id)} boshqa so'rov bilan o'zgargan ` +
`(joriy versiya ${joriy}) — qayta o'qing`,
`"${joriy}"`,
);
}
return n.qiymat;
}
@Injectable()
export class VazifalarService {
constructor(
@Inject(VAZIFA_OMBORI) private readonly ombor: VazifaOmbori,
@Inject(LOG) private readonly log: Log,
// vazifa bajarilganda tashqi xabar (webhook); provider qiymati
// null bo'lsa — yuborilmaydi
@Optional() @Inject(XABARCHI)
private readonly xabarchi: Xabarchi | null = null,
) {}
royxat(sorov: RoyxatSorovi): Promise<RoyxatNatija> {
return this.ombor.royxat(sorov);
}
teglar(): Promise<TegSoni[]> {
return this.ombor.teglar();
}
async ol(id: number): Promise<Vazifa> {
const v = await this.ombor.ol(id);
if (!v) throw topilmadi(id);
return v;
}
async yarat(yangi: YangiVazifa): Promise<Vazifa> {
if ((await this.ombor.soni({})) >= MAKS_VAZIFA) {
throw new KonfliktXatosi(
`Ro'yxat to'lgan (${String(MAKS_VAZIFA)})`,
{ kod: "royxat-toldi", sarlavha: "Vazifalar soni chegarada" },
);
}
const v = await this.ombor.yarat(yangi);
// joriyLog — JORIY so'rov logger'i (AsyncLocalStorage):
// sorovId avtomatik qo'shiladi
joriyLog(this.log).info({ vazifaId: v.id }, "vazifa yaratildi");
return v;
}
async yangila(
id: number, ozgarish: VazifaOzgarishi, shart?: Shart,
): Promise<Vazifa> {
// Faqat false → true o'tishida xabar: avvalgi holatni bilish
// kerak
const avval =
this.xabarchi !== null && ozgarish.bajarildi === true
? await this.ombor.ol(id)
: undefined;
const v = natija(
id, await this.ombor.yangila(id, ozgarish, shart),
);
if (
this.xabarchi !== null && avval?.bajarildi === false &&
v.bajarildi
) {
this.xabarchi("vazifa.bajarildi", v);
}
return v;
}
/** PUT: butun resurs (matn va bajarildi) almashtiriladi —
* idempotent */
almashtir(
id: number, toliq: ToliqVazifa, shart?: Shart,
): Promise<Vazifa> {
return this.yangila(id, toliq, shart);
}
/** PUT /v1/vazifalar — butun to'plam (frontend "almashtir") */
almashtirHammasi(
vazifalar: readonly AlmashtirishVazifasi[],
): Promise<{ vazifalar: Vazifa[]; versiya: number }> {
return this.ombor.almashtirHammasi(vazifalar);
}
/** Bulk yaratish: yaroqlilari yaratiladi, qolganlari — xato bilan
* (qisman muvaffaqiyat) */
async toplamYarat(
yangilar: readonly YangiVazifa[],
): Promise<Vazifa[]> {
const soni = await this.ombor.soni({});
if (soni + yangilar.length > MAKS_VAZIFA) {
throw new KonfliktXatosi(
`Ro'yxat to'ladi (${String(MAKS_VAZIFA)})`,
{ kod: "royxat-toldi", sarlavha: "Vazifalar soni chegarada" },
);
}
const yaratilgan: Vazifa[] = [];
for (const y of yangilar) {
yaratilgan.push(await this.ombor.yarat(y));
}
return yaratilgan;
}
async ochir(id: number, shart?: Shart): Promise<void> {
natija(id, await this.ombor.ochir(id, shart));
joriyLog(this.log).info({ vazifaId: id }, "vazifa o'chirildi");
}
}Klassda HTTP haqida bitta so'z ham yo'q: na req, na res, na status kod. Xatolar — 25-qismdagi xato klasslari (Xato turlari); ularni HTTP javobiga aylantirish filtrning ishi (keyingi dars). toplamYarat dagi for … await — ketma-ket yaratish, 25–27 dagidek. Bu yerda yashiringan eski xato bor, uni keyinroq Tranzaksiyalar NestJS'da darsida topamiz.
11.3 eksport.service.ts
// eksport.service.ts — uzoq davom etuvchi operatsiya (25-qism
// eksport.ts mantiqi): ishlar xotirada (bitta jarayon), fonda
// bajariladi. Provider sukut bo'yicha SINGLETON — `ishlar` Map'i
// hamma so'rovlar uchun bitta
import { randomUUID } from "node:crypto";
import { setImmediate as navbat } from "node:timers/promises";
import { Inject, Injectable } from "@nestjs/common";
import { IlovaXatosi } from "../umumiy/xatolar.ts";
import { csvQator } from "../umumiy/csv.ts";
import { joriyLog } from "../umumiy/log.ts";
import type { Log } from "../umumiy/log.ts";
import { LOG, VAZIFA_OMBORI } from "../tokenlar.ts";
import type {
KursorQiymati, VazifaOmbori,
} from "../vazifalar/ombor.ts";
import type { EksportSorovi } from "../vazifalar/sxema.ts";
import type { Vazifa } from "../vazifalar/vazifa.ts";
export type EksportHolati =
"kutilmoqda" | "bajarilmoqda" | "tayyor" | "xato";
export type Ish = {
id: string;
sorov: EksportSorovi;
holat: EksportHolati;
yaratildi: string;
tayyor: number;
jami: number;
natija?: string;
xato?: string;
tugash: number;
};
const USTUNLAR = [
"id", "matn", "bajarildi", "teglar", "yaratildi", "yangilandi",
] as const;
const SAQLASH_MS = 60 * 60 * 1000; // natija 1 soat saqlanadi
const BOLAK = 100;
/** discriminatedUnion natijasi: sorov.format bo'yicha TS har tarmoqda
* to'g'ri turni biladi (ajratgich faqat csv'da) */
function yoz(
sorov: EksportSorovi,
vazifalar: readonly Vazifa[],
birinchi: boolean,
): string {
if (sorov.format === "json") {
return vazifalar.map(
(v, i) => (birinchi && i === 0 ? "" : ",") + JSON.stringify(v),
).join("");
}
return vazifalar.map((v) => csvQator(
[v.id, v.matn, v.bajarildi, v.teglar.join(" "), v.yaratildi,
v.yangilandi],
sorov.ajratgich,
)).join("");
}
@Injectable()
export class EksportService {
readonly #ishlar = new Map<string, Ish>();
/** tikuvlar (seam): testda soxta soat */
soat: () => number = Date.now;
constructor(
@Inject(VAZIFA_OMBORI) private readonly ombor: VazifaOmbori,
@Inject(LOG) private readonly log: Log,
) {}
boshla(sorov: EksportSorovi): Ish {
const ish: Ish = {
id: randomUUID(), sorov, holat: "kutilmoqda",
yaratildi: new Date(this.soat()).toISOString(),
tayyor: 0, jami: 0, tugash: this.soat() + SAQLASH_MS,
};
this.#ishlar.set(ish.id, ish);
// Javob kutmaydi: ish fonda; xatosi ish holatiga yoziladi (yutib
// yuborilmaydi). Fon ishi ham shu so'rov konteksti (sorovId)
// bilan loglanadi
const l = joriyLog(this.log);
this.#bajar(ish).then(
() => {
l.info(
{ eksportId: ish.id, soni: ish.tayyor }, "eksport tayyor",
);
},
(x: unknown) => {
ish.holat = "xato";
ish.xato = "Eksport bajarilmadi";
l.error({ err: x, eksportId: ish.id }, "eksport bajarilmadi");
},
);
return ish;
}
async #bajar(ish: Ish): Promise<void> {
ish.holat = "bajarilmoqda";
ish.jami = await this.ombor.soni({});
let chiqish = ish.sorov.format === "json"
? "["
: csvQator(USTUNLAR, ish.sorov.ajratgich);
let keyin: KursorQiymati | undefined;
for (;;) {
const n = await this.ombor.royxat({
filtr: {}, saralash: [], limit: BOLAK,
sahifa: keyin === undefined
? { tur: "kursor" }
: { tur: "kursor", keyin },
});
chiqish += yoz(ish.sorov, n.vazifalar, ish.tayyor === 0);
ish.tayyor += n.vazifalar.length;
if (!n.borKeyingi || n.keyingi === undefined) break;
keyin = n.keyingi;
// event loop'ni bo'shatamiz — boshqa so'rovlar kutib qolmasin
await navbat();
}
ish.natija =
ish.sorov.format === "json" ? chiqish + "]" : chiqish;
ish.holat = "tayyor";
ish.tugash = this.soat() + SAQLASH_MS;
}
topilsin(id: string): Ish {
for (const [k, i] of this.#ishlar) {
if (i.tugash <= this.soat()) this.#ishlar.delete(k);
}
const ish = this.#ishlar.get(id);
if (ish === undefined) {
throw new IlovaXatosi(
404, `Eksport ${id} topilmadi (yoki muddati o'tgan)`,
{ kod: "eksport-topilmadi", sarlavha: "Eksport topilmadi" },
);
}
return ish;
}
korinish(ish: Ish, asos: string): Record<string, unknown> {
const k: Record<string, unknown> = {
id: ish.id, format: ish.sorov.format, holat: ish.holat,
yaratildi: ish.yaratildi, tayyor: ish.tayyor, jami: ish.jami,
};
if (ish.holat === "tayyor") k.fayl = `${asos}/${ish.id}/fayl`;
if (ish.xato !== undefined) k.xato = ish.xato;
return k;
}
}25-qismdagi eksport.ts ikki ishni qilardi: routerni yasardi va ishlarni boshqarardi. Endi HTTP qismi — o'tgan darsdagi EksportController da, ishlar — shu xizmatda. Xizmatdagi #ishlar va #bajar — private maydon va metodlar: ularni klass tashqarisidan hech kim o'zgartira olmaydi. soat esa ataylab ochiq — testda soxta vaqt berish uchun tikuv (seam, Mock, stub, spy va fake).
11.4 bogliqliklar.module.ts
Ombor, log va xabarchi main.ts da Nest'dan oldin yaratiladi — 27-qismdagi omborYarat o'zgarmaydi. Ularni konteynerga berish uchun useValue provider'lar:
// bogliqliklar.module.ts — tashqarida yaratilgan obyektlar (ombor,
// log, xabarchi) DI konteyneriga beriladi: useValue provider'lar.
// @Global — har modul import qilmasdan ishlatadi; forRoot(...) —
// dinamik modul (keyinroq batafsil)
import { Global, Module } from "@nestjs/common";
import type { DynamicModule } from "@nestjs/common";
import {
ILOVA_SOZLAMALARI, LOG, VAZIFA_OMBORI, XABARCHI,
} from "./tokenlar.ts";
import type { IlovaSozlamalari } from "./tokenlar.ts";
import type { VazifaOmbori } from "./vazifalar/ombor.ts";
import type { Log } from "./umumiy/log.ts";
import type { Xabarchi } from "./umumiy/webhook.ts";
export type TashqiBogliqliklar = {
ombor: VazifaOmbori;
log: Log;
xabarchi?: Xabarchi | undefined;
sozlamalar: IlovaSozlamalari;
};
@Global()
@Module({})
export class BogliqliklarModule {
static forRoot(
{ ombor, log, xabarchi, sozlamalar }: TashqiBogliqliklar,
): DynamicModule {
const providers = [
{ provide: VAZIFA_OMBORI, useValue: ombor },
{ provide: LOG, useValue: log },
// xabarchi ixtiyoriy: yo'q bo'lsa null (xizmat @Optional bilan
// oladi)
{ provide: XABARCHI, useValue: xabarchi ?? null },
{ provide: ILOVA_SOZLAMALARI, useValue: sozlamalar },
];
return {
module: BogliqliklarModule,
providers,
exports: providers.map((p) => p.provide),
};
}
}Uch yangi narsa, uchalasi keyingi darslarda chuqur:
{ provide: VAZIFA_OMBORI, useValue: ombor }— "shu token so'ralsa, mana bu tayyor obyektni ber". Nest uni yaratmaydi, faqat beradi (Custom provider'lar).@Global()— Modullar darsidagi global modul: xizmatlarBogliqliklarModuleni import qilmasdan oladi.static forRoot(...)— modulni qiymatlar bilan qaytaradi.@Module({})bo'sh: tarkib ish vaqtida beriladi (Dynamic modullar).
exports: providers.map((p) => p.provide) — hamma tokenni eksport qiladi; ro'yxatni ikki marta yozmaslik uchun.
11.5 Modullar
src/vazifalar/vazifalar.module.ts:
// vazifalar.module.ts — vazifalar xususiyat moduli: controller'lar +
// xizmat. Ombor, log — global BogliqliklarModule dan (token bilan)
import { Module } from "@nestjs/common";
import { VazifalarController } from "./vazifalar.controller.ts";
import { TeglarController } from "./teglar.controller.ts";
import { VazifalarService } from "./vazifalar.service.ts";
@Module({
controllers: [VazifalarController, TeglarController],
providers: [VazifalarService],
// boshqa modul (masalan eksport) xizmatdan foydalanmoqchi bo'lsa —
// eksport qilinadi
exports: [VazifalarService],
})
export class VazifalarModule {}src/eksport/eksport.module.ts:
// eksport.module.ts — eksport xususiyat moduli
import { Module } from "@nestjs/common";
import { EksportController } from "./eksport.controller.ts";
import { EksportService } from "./eksport.service.ts";
@Module({
controllers: [EksportController],
providers: [EksportService],
})
export class EksportModule {}BogliqliklarModule hali hech qayerga ulanmagan. Uni AppModule ga ulash main.ts ning o'zgarishini talab qiladi — keyingi darsda.
11.6 Tekshiruv: bitta kutilgan xato
npm run tip
npm run lint
npm testBiz shu holatni kanon nusxasida tekshirdik (Node 24.21.0, NestJS 12.2.0). tip — 0 xato: o'tgan darsdagi to'rt TS2307/TS7006 yo'qoldi. lint — 0. npm test esa (Vitest hozircha faqat src/**/*.spec.ts ni yurgizadi — 2-darsdagi salom.controller.spec.ts) yiqiladi:
FAIL src/salom.controller.spec.ts [ src/salom.controller.spec.ts ]
Error: Nest can't resolve dependencies of the VazifalarService (?, Symbol(LOG), Symbol(XABARCHI)). Please make sure that the argument Symbol(VAZIFA_OMBORI) at index [0] is available in the VazifalarModule module.
Potential solutions:
- Is VazifalarModule a valid NestJS module?
- If Symbol(VAZIFA_OMBORI) is a provider, is it part of the current VazifalarModule?
- If Symbol(VAZIFA_OMBORI) is exported from a separate @Module, is that module imported within VazifalarModule?
@Module({
imports: [ /* the Module containing Symbol(VAZIFA_OMBORI) */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
Test Files 1 failed (1)
Tests 2 skipped (2)Tarjimasi: "Nest VazifalarService ning bog'liqliklarini topa olmadi (?, Symbol(LOG), Symbol(XABARCHI)). VazifalarModule modulida 0-indeksdagi Symbol(VAZIFA_OMBORI) argumenti mavjudligiga ishonch hosil qiling."
Xabarni o'qishni mashq qilamiz:
- Qavs ichida uchta argument:
?— topilmagani, keyingi ikkitasi — tokenlar. Tokenli argument klass nomi emas,Symbol(...)bo'lib ko'rinadi —Symbol("LOG")dagi tavsif shu yerda kerak bo'ldi. - Xabar faqat birinchi topilmaganini aytadi.
LOGham hozir yo'q, lekin Nest 0-argumentda to'xtadi. in the VazifalarModule module— xizmat shu modulda yaratilmoqda va token na shu modulda, na global modullarda yo'q. Sabab bizga ma'lum:BogliqliklarModulehali daraxtga ulanmagan.
Test AppModule ni ko'taradi, AppModule esa VazifalarModule ni. Faqat shu xato bo'lsa — hammasi to'g'ri. Boshqa xato chiqsa (masalan, tip da TS2305 yoki Nest xabarida boshqa klass nomi), uni hozir tuzating.
Express API bu paytda ishlashda davom etadi: biz npm run start:express bilan yurgizib, POST /v1/vazifalar yubordik — 201 Created. Nest qismi esa bootstrap'ni kutyapti. Hali commit qilmang — commit Bootstrap: main.ts darsida, butun API ko'chgandan keyin.
12. Real ishda
- Har Nest loyihasida
*.service.ts— eng ko'p fayl. Jamoa qoidasi odatda: controller yupqa, xizmat qalin; xizmat HTTP'ni bilmaydi. - Kod review'da tez-tez uchraydigan izohlar: "bu klassda
@Injectable()yo'q", "xizmatdanewbilan ombor yaratilmasin", "singleton'da so'rov holati saqlanmasin". - Yordamchilar papkasi (
utils/,umumiy/) — toza funksiyalar uchun. Ularni provider qilish ham,staticmetodli klassga yig'ish ham shart emas. - Intervyu: "Provider nima?", "Nest provider'ining sukut scope'i qaysi?" (singleton), "
@Injectable()nima uchun kerak?", "Interfeysni qanday inject qilasiz?" (token +@Inject).
Xulosa
- Provider — Nest yaratib beradigan obyekt; xizmat — biznes qoidalari yashaydigan
@Injectable()klass. - Klass
providersda bo'lishi va (kerak bo'lsa)exportsqilinishi shart; yorliq yolg'iz yetmaydi. - Constructor'da
private readonly x: Klass— Nest turidan taniydi; interfeys uchun@Inject(TOKEN), ixtiyoriy uchun@Optional(). - Provider sukut bo'yicha singleton: bitta nusxa, hamma so'rovga umumiy — so'rov holati unga yozilmaydi.
@Injectable()unutilsa ilova ko'tariladi, xato esa so'rovdaundefinedbo'lib chiqadi.- Toza funksiya (holatsiz, tashqi bog'liqliksiz) — oddiy yordamchi bo'lib qoladi.
Keyingi dars: Bootstrap: main.ts va ilova sozlamalari — NestFactory.create, Express middleware'lari, versiyalash va xato filtri; eski Express fayllarini o'chiramiz, testlarni Vitest'ga o'tkazamiz va commit qilamiz.
Manbalar
- NestJS hujjati — Providers (
@Injectable, constructor injection, singleton,@Optional): https://docs.nestjs.com/providers - NestJS hujjati — Custom providers (
useValue, tokenlar): https://docs.nestjs.com/fundamentals/custom-providers - TypeScript hujjati — Parameter properties: https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
- Kanon (IlmHamroh, 2026-10-27):
vazifalar-apixizmatlari, NestJS 12.2.0. Singleton, provider'siz modul va@Injectable()siz klass tajribalari, oraliq holatdagitip/lint/Vitest chiqishi va Express API tekshiruvi — biz ishga tushirdik (2026-11-02, NestJS 12.2.0, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!