IlmHamroh
JavaScript Full-stack/30-qism. NestJS3/65-dars19 daqiqa
Mundarija (28)

NestJS modullari: @Module, imports, providers, exports va @Global

Qisqacha: Modul — @Module() dekoratorli klass: controllers (marshrutlar), providers (Nest yaratadigan obyektlar), imports (boshqa modullar) va exports (tashqariga beriladigan provider'lar). Provider sukut bo'yicha faqat o'z modulida ko'rinadi. Boshqa modul uni ishlatishi uchun ikki shart: egasi uni exports ga yozadi, ishlatuvchi esa egasini imports ga yozadi. Shart bajarilmasa, Nest ilova ishga tushayotganda Nest can't resolve dependencies… xatosini beradi.

Bu darsda

  • @Module ning to'rt maydonini va har biri nimani hal qilishini bilasiz.
  • Provider qachon boshqa modulda ko'rinishini va qachon ko'rinmasligini haqiqiy xato bilan tushuntirasiz.
  • Shared, @Global va qayta eksport qiluvchi (re-export) modullarni farqlaysiz.
  • Bir provider'ni ikki modulga yozib qo'yish nega ikki nusxa berishini o'z ko'zingiz bilan ko'rasiz.
  • vazifalar-api ni modullarga bo'lishni boshlaysiz.

Oldin bilishingiz kerak: NestJS nima va nega, Nest CLI va loyiha tuzilmasi, express.Router va qatlamli loyiha tuzilmasi, Nega modullar va ESM export.

O'tgan darsda vazifalar-api ga Nest skeleti qo'shildi: bitta AppModule va GET /salom. Endi haqiqiy marshrutlar keladi. Ularni bitta modulga to'kib tashlamaymiz — avval "qaysi kod qaysi qutida" degan savolga javob beramiz.

1. Nega bu kerak?

25-qismda kodni feature-folder bo'yicha ajratgandik: vazifalar/, eksport/, umumiy/ (qatlamlar darsi). Bu tartib faqat kelishuv edi. eksport/eksport.ts xohlasa vazifalar/xizmat.ts ning ichki funksiyasini import qila olardi. Hech kim to'xtatmasdi.

Loyiha kattalashganda bunday "yashirin yo'laklar" ko'payadi. Bir papkadagi o'zgarish boshqasini kutilmaganda buzadi. Kim nimaga tayanishini bilish uchun butun kodni o'qish kerak bo'ladi.

Nest moduli — kelishuvni qoidaga aylantiradi. Har modul o'z ichidagi obyektlarni sukut bo'yicha yashiradi. Tashqariga faqat ochiq e'lon qilinganlari chiqadi.

O'xshatish: ko'p qavatli uydagi xonadonlar. Har xonadonning o'z oshxonasi, o'z jihozlari bor. Qo'shni sizning muzlatkichingizdan foydalana olmaydi. Lekin uyning umumiy liftini hamma ishlatadi — u "umumiy" deb e'lon qilingan. Nest'da xonadon — modul, jihoz — provider, lift — eksport qilingan provider.

2. @Module ning to'rt maydoni

2.1 Jadval

Maydon Nima yoziladi Savol
controllers shu modul marshrutlari "Qaysi HTTP yo'llar shu yerda?"
providers Nest yaratadigan klasslar "Nimani shu modul o'zi yaratadi?"
imports boshqa modullar "Kimning eksportlari kerak?"
exports o'z provider'laridan bir qismi "Tashqariga nimani beraman?"

nest g resource yaratgan modul (o'tgan darsda ko'rdik) — eng sodda shakl:

ts
@Module({
  controllers: [MahsulotlarController],
  providers: [MahsulotlarService],
})
export class MahsulotlarModule {}

Bu yerda exports yo'q. Ya'ni MahsulotlarService faqat shu modul ichida — MahsulotlarController uchun. Boshqa modul uni ololmaydi.

Klassning o'zi bo'sh: {}. Hamma ma'lumot dekoratorda. Klass faqat tashuvchi — Nest uning ustidagi metama'lumotni o'qiydi. O'tgan darsdagi ESLint qoidasi (allowWithDecorator) shu uchun kerak edi.

2.2 Modul daraxti

Har Nest ilovasida bitta ildiz modul bor — odatda AppModule. NestFactory.create(AppModule) undan boshlaydi, imports bo'ylab pastga tushadi va butun daraxtni yig'adi. Daraxtga ulanmagan modul ilovada umuman yo'q.

Ko'chish tugagach vazifalar-api ning daraxti shunday bo'ladi:

flowchart TB
  A["AppModule (ildiz)"] --> B["BogliqliklarModule (@Global)"]
  A --> V["VazifalarModule"]
  A --> E["EksportModule"]
  B -. "ombor, log, sozlamalar" .-> V
  B -. "ombor, log" .-> E
  V --> VC["VazifalarController, TeglarController"]
  V --> VS["VazifalarService (exports)"]
  E --> EC["EksportController"]
  E --> ES["EksportService"]

Diagrammadagi ikki xil chiziqqa qarang. To'liq chiziq — imports va modul ichidagi narsalar. Punktir — @Global moduldan import qilmasdan keladigan provider'lar (ombor, log). Bu daraxt keyingi uch darsda to'liq quriladi; bu darsda modul qutilarining o'zini yasaymiz.

3. Ko'rinish qoidasi — haqiqiy tajriba

3.1 Tajriba sharti

Qoidani so'z bilan aytish oson, tushunish qiyin. Shuning uchun Nest'da tajriba qildik (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0). Ikki xizmat:

ts
@Injectable()
class SoatService {
  hozir(): string {
    return '2026-11-01T09:00:00Z';
  }
}

@Injectable()
class HisobotService {
  constructor(private readonly soat: SoatService) {}
  sarlavha(): string {
    return `Hisobot (${this.soat.hozir()})`;
  }
}

@Injectable() — "bu klassni Nest yarata oladi" degan yorliq; keyingi darsdan keyingisida batafsil. HisobotService ga SoatService kerak. Endi ularni turli modullarga joylab, Nest nima deyishini ko'ramiz.

3.2 exports siz

ts
@Module({ providers: [SoatService] })
class SoatYopiqModule {}

@Module({ imports: [SoatYopiqModule], providers: [HisobotService] })
class Hisobot1Module {}

Hisobot1Module SoatYopiqModule ni import qildi. Lekin SoatYopiqModule SoatService ni eksport qilmagan. Natija:

text
Nest can't resolve dependencies of the HisobotService (?). Please make sure that the argument SoatService at index [0] is available in the Hisobot1Module module.

Potential solutions:
- Is Hisobot1Module a valid NestJS module?
- If SoatService is a provider, is it part of the current Hisobot1Module?
- If SoatService is exported from a separate @Module, is that module imported within Hisobot1Module?
  @Module({
    imports: [ /* the Module containing SoatService */ ]
  })

For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors

Tarjimasi: "Nest HisobotService ning bog'liqliklarini topa olmadi (?). Hisobot1Module modulida 0-indeksdagi SoatService argumenti mavjudligiga ishonch hosil qiling."

Xabarni qism-qism o'qing:

  • HisobotService (?) — qavs ichida constructor argumentlari. ? — topilmagani. Ikki argument bo'lsa va ikkinchisi topilmasa — (SoatService, ?).
  • at index [0] — constructor'dagi nechanchi parametr.
  • in the Hisobot1Module module — qaysi modul ichida qidirildi. Bu eng muhim qism: xato SoatService da emas, Hisobot1Module ning ko'rish doirasida.
  • Uchta maslahat — tekshirish ro'yxati. Bizning holatga uchinchisi yaqin, lekin to'liq emas: modul import qilingan, eksport esa yo'q.

Xato ilova ishga tushayotganda chiqdi, birinchi so'rovda emas. Bu yaxshi: noto'g'ri ulangan ilova umuman ko'tarilmaydi (fail-fast).

3.3 exports bilan — va bitta nusxa

ts
@Module({ providers: [SoatService], exports: [SoatService] })
class SoatModule {}

@Module({ imports: [SoatModule], providers: [HisobotService] })
class Hisobot2Module {}

@Module({ imports: [SoatModule], providers: [EksportService] })
class Eksport2Module {}

@Module({ imports: [Hisobot2Module, Eksport2Module] })
class Ilova2Module {}

Endi ikki modul SoatModule ni import qildi. EksportService ham constructor'da SoatService oladi. Tajriba ikkala xizmatdagi SoatService ni === bilan solishtirdi:

text
2) eksport bilan: OK
   Hisobot (2026-11-01T09:00:00Z)
   bitta SoatService: true

true — ikki modul bitta SoatService nusxasini oldi. Modul import qilinganda nusxa ko'chirilmaydi. Butun ilovada SoatModule bitta, uning provider'lari ham bitta. Bunday — ko'p joyda ishlatiladigan — modul shared module (umumiy modul) deyiladi. Nest'da har modul sukut bo'yicha shared: uni qancha modul import qilsa ham, nusxa bitta.

3.4 Tuzoq: ikki joyda providers

Yangi boshlovchi ko'pincha "ko'rinmayaptimi — o'zimning providers ga qo'shaman" deydi:

ts
@Module({ providers: [SoatService, HisobotService] })
class Hisobot5Module {}

@Module({ providers: [SoatService, EksportService] })
class Eksport5Module {}

Xato yo'qoladi. Lekin natija:

text
5) ikki joyda providers: OK
   bitta SoatService: false

false — endi ilovada ikkita SoatService bor. Har modul o'z providers idagi klassdan o'z nusxasini yaratdi. Soat uchun bu zararsiz tuyuladi. Lekin vazifalar-api dagi EksportService ichida eksport ishlari Map da saqlanadi. Ikki nusxa bo'lsa — bir controller boshlagan ishni ikkinchisi "topilmadi" deydi. Xato jim chiqadi, ishga tushishda hech narsa demaydi.

Diqqat: Provider ko'rinmasa, uni ikkinchi modulning providers iga ko'chirmang. To'g'ri yo'l: egasida exports, ishlatuvchida imports.

3.5 providers da yo'q narsani eksport qilish

ts
@Module({ exports: [SoatService] })
class NotogriModule {}
text
Nest cannot export a provider/module that is not a part of the currently processed module (NotogriModule). Please verify whether the exported SoatService is available in this particular context.

Tarjimasi: "Nest joriy modulning (NotogriModule) qismi bo'lmagan provider yoki modulni eksport qila olmaydi." Eksport — o'zingizda bor narsani berish. Yo'q narsani berib bo'lmaydi.

Tekshirib ko'ring: A modul B ni import qildi, B esa C ni import qilib, CService ni ishlatadi. C CService ni eksport qilgan. A ichidagi xizmat CService ni ola oladimi?

Javob

Yo'q. Import o'tkazuvchan emas: A faqat B ning eksportlarini ko'radi. B CService ni o'zi ishlatadi, lekin tashqariga bermagan. A ga kerak bo'lsa — yo A C ni o'zi import qiladi, yo B C ni qayta eksport qiladi (keyingi bo'lim).

4. JS modelida ko'rinish qoidasi

Nest ichida nima bo'layotganini kichik modelda takrorlaymiz. Bu blok brauzerda ishlaydi. Provider — oddiy obyekt: nomi va unga kerak provider'lar ro'yxati. Modul — to'rt maydonli obyekt:

js
const Soat = { nomi: "SoatService", kerak: [] };
const Hisobot = { nomi: "HisobotService", kerak: [Soat] };

function modul(nomi, sozlama) {
  return {
    nomi, providers: [], imports: [], exports: [], ...sozlama,
  };
}

// Modul ichida ko'rinadi: o'z provider'lari + import qilinganlarning
// eksportlari
function korinadi(m) {
  const royxat = new Set(m.providers);
  for (const boshqa of m.imports) {
    for (const p of boshqa.exports) royxat.add(p);
  }
  return royxat;
}

function yigish(m) {
  for (const p of m.providers) {
    p.kerak.forEach((dep, i) => {
      if (!korinadi(m).has(dep)) {
        throw new Error(`${p.nomi} ning ${i}-argumenti ` +
          `(${dep.nomi}) ${m.nomi} da ko'rinmaydi`);
      }
    });
  }
  return `${m.nomi}: OK`;
}

const SoatYopiq = modul("SoatYopiq", { providers: [Soat] });
const SoatOchiq = modul("SoatOchiq", {
  providers: [Soat],
  exports: [Soat],
});

for (const ichki of [SoatYopiq, SoatOchiq]) {
  const h = modul(`Hisobot(${ichki.nomi})`, {
    imports: [ichki],
    providers: [Hisobot],
  });
  try {
    console.log(yigish(h));
  } catch (e) {
    console.log(e.message);
  }
}

Konsolda:

text
HisobotService ning 0-argumenti (SoatService) Hisobot(SoatYopiq) da ko'rinmaydi
Hisobot(SoatOchiq): OK

korinadi funksiyasi — butun qoidaning yuragi. Unda faqat ikki manba bor: modulning o'z providers i va import qilingan modullarning exports i. Import qilingan modulning ichidagi boshqa narsalar bu ro'yxatga tushmaydi. Nest xabari ham aynan shu ikki manbani tekshirishni maslahat bergandi.

Model haqiqiy Nest'dan soddaroq: unda @Global va qayta eksport yo'q. Ularni mashqlarda qo'shasiz.

5. Modul turlari

5.1 Feature modul

Feature modul (xususiyat moduli) — bitta biznes mavzusi: VazifalarModule, EksportModule, keyinroq AuthModule. Ichida o'sha mavzuning controller'lari va xizmatlari. Odatda ko'p narsani eksport qilmaydi — faqat boshqa modullarga haqiqatan kerak bo'lganini.

vazifalar-api da VazifalarModule o'z xizmatini eksport qiladi — boshqa modul (masalan, keyinroq eksport yoki bildirishnomalar) vazifalar mantig'iga muhtoj bo'lsa, VazifalarModule ni import qiladi.

5.2 @Global modul

Ba'zi provider'lar deyarli hamma joyda kerak: log, sozlamalar, ma'lumotlar bazasi ulanishi. Har modulda imports: [LogModule] yozish charchatadi. @Global() dekoratori modulni global qiladi: uning eksportlari hamma modulda import qilmasdan ko'rinadi.

ts
@Global()
@Module({ providers: [SoatService], exports: [SoatService] })
class SoatGlobalModule {}

@Module({ providers: [HisobotService] })
class Hisobot3Module {}

@Module({ imports: [SoatGlobalModule, Hisobot3Module] })
class Ilova3Module {}

Hisobot3Module SoatGlobalModule ni import qilmagan. Natija:

text
3) @Global: OK
   Hisobot (2026-11-01T09:00:00Z)

Ishladi. Lekin e'tibor bering: global modulni ham bir marta — odatda ildizda — import qilish kerak (Ilova3Module da). Aks holda u daraxtda yo'q.

vazifalar-api da bitta global modul bo'ladi — BogliqliklarModule: ombor, log, xabarchi va sozlamalar. Ular main.ts da, Nest'dan oldin yaratiladi (25-qismdagi omborYarat o'zgarmaydi) va global modul orqali hamma xizmatga beriladi. Qanday berilishi — Custom provider'lar va Dynamic modullar mavzusi.

Diqqat: @Global ni ko'p ishlatmang. Hammasi global bo'lsa, modul chegarasi yana yo'qoladi — «Nega bu kerak?» bo'limidagi muammoga qaytasiz. Nest hujjati ham shuni ogohlantiradi: global modul — faqat haqiqatan hamma joyda kerak bo'lgan narsa uchun. Qolganida imports ochiq yozilsin — kim nimaga tayanishi ko'rinib tursin.

5.3 Qayta eksport (re-export)

Modul o'zi import qilgan modulni qayta eksport qila oladi. Bu ko'p kichik modullarni bitta "paket" qilib berish uchun qulay:

ts
@Module({ imports: [SoatModule], exports: [SoatModule] })
class UmumiyModule {}

@Module({ imports: [UmumiyModule], providers: [HisobotService] })
class Hisobot4Module {}
text
4) qayta eksport: OK
   Hisobot (2026-11-01T09:00:00Z)

Hisobot4Module faqat UmumiyModule ni bildi, lekin SoatService ni oldi. exports ga provider emas, butun modul yozildi — uning hamma eksportlari o'tdi. Real loyihada bu CoreModule yoki SharedModule nomi bilan uchraydi: log, config, baza ulanishi — bitta importda.

5.4 Dinamik modul — bir qarash

Ko'chish tugagan vazifalar-api da AppModule shunday ko'rinadi:

ts
@Module({})
export class AppModule {
  static forRoot(b: TashqiBogliqliklar): DynamicModule {
    return {
      module: AppModule,
      imports: [
        BogliqliklarModule.forRoot(b),
        VazifalarModule,
        EksportModule,
      ],
    };
  }
}

forRoot(...) — statik metod, modulni sozlama bilan qaytaradi. Bu — dinamik modul (dynamic module): modul tarkibi ish vaqtida beriladi. Bu darsda faqat shuni bilish yetadi: @Module({...}) dagi to'rt maydonni metod ham qaytara oladi. Chuqur — Dynamic modullar darsida.

Tekshirib ko'ring: Log xizmati uchun LogModule ni @Global qildingiz, lekin AppModule ning imports iga yozmadingiz. Xizmatlar logni ola oladimi?

Javob

Yo'q. @Global — "eksportlarim hamma joyda ko'rinsin" degani, "men o'zim daraxtga qo'shilaman" degani emas. Daraxtga ulanmagan modulni Nest umuman bilmaydi. Global modul ham bir marta, odatda ildiz modulda, import qilinadi.

6. Modul fayl importi bilan bir narsa emas

Bir chalkashlikni oldindan yechaylik. vazifalar.controller.ts ichida import { VazifalarService } from "./vazifalar.service.ts" yozilgan. Bu ESM import (Nega modullar va ESM export) — TypeScript va Node uchun: "klass qayerda yozilgan". Nest moduli esa boshqa savolga javob beradi: "DI konteyner shu klassdan nusxani kimga bera oladi".

ESM import Nest imports
Kim tekshiradi TypeScript, Node Nest DI konteyner
Qachon kompilyatsiya, yuklash NestFactory.create
Nima uchun klassning kodini olish klassning nusxasini olish
Xato TS2307, ERR_MODULE_NOT_FOUND Nest can't resolve dependencies…

Ikkalasi ham kerak. Klassni ESM bilan import qilib, constructor'ga yozasiz — TypeScript rozi. Lekin Nest modul qoidasi buzilgan bo'lsa, ilova ishga tushmaydi. «Ko'rinish qoidasi» bo'limidagi tajribada ham fayl importlari to'g'ri edi.

7. Ishga tushish logida modullar

Nest logi modullarni ko'rsatadi. «Loyiha qadami» bo'limidagi o'zgarishdan keyin vazifalar-api ni build qilib yurgizdik (biz ishga tushirdik; jarayon raqami va vaqt olib tashlandi):

text
LOG [NestFactory] Starting Nest application...
LOG [InstanceLoader] VazifalarModule dependencies initialized +8ms
LOG [InstanceLoader] EksportModule dependencies initialized +0ms
LOG [InstanceLoader] AppModule dependencies initialized +0ms
LOG [RoutesResolver] SalomController {/salom}: +7ms
LOG [RouterExplorer] Mapped {/salom, GET} route +3ms
LOG [NestApplication] Nest application successfully started +1ms
vazifalar-api (Nest): http://127.0.0.1:4593

Tartibga qarang: avval ichki modullar (VazifalarModule, EksportModule), oxirida ildiz AppModule. Nest daraxtni pastdan yuqoriga yig'adi — xuddi 1-darsdagi JS konteyner bog'liqliklarni pastdan yuqoriga yaratgani kabi. Modul nimaga tayansa, o'sha oldin tayyor bo'ladi.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Nest can't resolve dependencies of the X (?)… in the YModule provider eksport qilinmagan yoki modul import qilinmagan egasida exports, Y da imports
Ma'lumot "yo'qolib qoladi", xato yo'q provider ikki modulning providers ida — ikki nusxa bitta modulda qoldiring, eksport qiling
Nest cannot export a provider/module that is not a part of… exports da providers da yo'q narsa avval providers ga yoki imports ga qo'shing
Global modul ishlamadi daraxtga hech qayerda import qilinmagan ildizda bir marta import
Controller marshrutlari yo'q (404) modul AppModule daraxtiga ulanmagan imports ga qo'shing; logda Mapped {…} ni tekshiring

Jadvaldagi ikkinchi qator eng xavflisi. Birinchi va uchinchi xato ilovani ishga tushirmaydi — siz uni darrov ko'rasiz. Ikki nusxa esa ishlab turgan ilovada sekin "o'zini ko'rsatadi": hisoblagich ikkiga bo'linadi, kesh yarim ishlaydi, eksport ishi "topilmadi" bo'ladi. Shunday g'alati xatoni ko'rsangiz — avval provider qaysi modullarning providers ida turganini qidiring.

Oxirgisi eng jim xato: ilova ko'tariladi, lekin marshrut yo'q. Logdagi RoutesResolver va Mapped qatorlari — birinchi tekshiruv joyi.

9. Mashqlar

Mashqlar kurs/mashqlar/30/03-modullar/ da.

1-mashq (oson): ko'rinadimi?

SmsModule SmsService ni providers ga yozgan, exports ga yozmagan. BildirishnomaModule SmsModule ni import qilgan. To'ldiring:

  • BildirishnomaService SmsService ni ola oladimi? —
  • Xabarda qaysi modul nomi turadi? —
  • Tuzatish uchun qaysi modulning qaysi maydoniga yozish kerak? SmsModule ning
  • SmsService ni BildirishnomaModule ning providers iga ham yozsak, ilovada nechta SmsService bo'ladi? —
Yechim
  • yo'q — import bor, eksport yo'q.
  • BildirishnomaModule — Nest bog'liqlikni ishlatuvchi modul ichida qidiradi: …is available in the BildirishnomaModule module.
  • exports — exports: [SmsService].
  • 2 — har providers o'z nusxasini yaratadi; xato yo'qoladi, lekin SMS hisoblagichi kabi holat ikkiga bo'linadi.

2-mashq (o'rta): modelga @Global qo'shing

Darsdagi JS modeliga global: true sozlamasini qo'shing. korinadi(m, globallar) endi ikkinchi argument oladi: global modullar ro'yxati, ularning eksportlari ham ko'rinadi. yigish(m, globallar) ham uni uzatsin. Sinov: SoatGlobal (global, Soat ni eksport qiladi) va Hisobot moduli (hech narsa import qilmaydi). Ishora: for (const boshqa of [...m.imports, ...globallar]) (spread).

Yechim
js
const Soat = { nomi: "SoatService", kerak: [] };
const Hisobot = { nomi: "HisobotService", kerak: [Soat] };

function modul(nomi, sozlama) {
  return {
    nomi, providers: [], imports: [], exports: [], global: false,
    ...sozlama,
  };
}

function korinadi(m, globallar) {
  const royxat = new Set(m.providers);
  for (const boshqa of [...m.imports, ...globallar]) {
    for (const p of boshqa.exports) royxat.add(p);
  }
  return royxat;
}

function yigish(m, globallar = []) {
  for (const p of m.providers) {
    p.kerak.forEach((dep, i) => {
      if (!korinadi(m, globallar).has(dep)) {
        throw new Error(`${p.nomi} ning ${i}-argumenti ` +
          `(${dep.nomi}) ${m.nomi} da ko'rinmaydi`);
      }
    });
  }
  return `${m.nomi}: OK`;
}

const SoatGlobal = modul("SoatGlobal", {
  providers: [Soat], exports: [Soat], global: true,
});
const H = modul("Hisobot", { providers: [Hisobot] });

const hamma = [SoatGlobal, H];
const globallar = hamma.filter((m) => m.global);
for (const g of [[], globallar]) {
  try {
    console.log(yigish(H, g));
  } catch (e) {
    console.log(e.message);
  }
}

Konsolda:

text
HisobotService ning 0-argumenti (SoatService) Hisobot da ko'rinmaydi
Hisobot: OK

Birinchi yugurishda global ro'yxat bo'sh — "daraxtga ulanmagan global modul" holati. Ikkinchisida SoatGlobal daraxtda va global — Hisobot import qilmasdan oldi. hamma massivi — ilova daraxtidagi modullar; Nest ham global modullarni faqat daraxtdan topadi.

3-mashq (qiyin): modul tekshiruvchisi

tekshirModul(m) funksiyasini yozing. U ikki xil xatoni topib, satrlar massivini qaytarsin (xato bo'lmasa — bo'sh massiv):

  1. exports dagi provider na providers da, na import qilingan modulning eksportida yo'q → "<modul>: <provider> eksport qilingan, lekin modulda yo'q".
  2. Provider bir nechta modulning providers ida uchraydi → "<provider> ikki marta yaratiladi: <modul1>, <modul2>". Buni butun ro'yxat uchun tekshiradigan takrorlar(modullar) funksiyasi qilsin.

Ishora: Map da provider → modul nomlari ro'yxatini yig'ing (Map).

Yechim
js
const Soat = { nomi: "SoatService" };
const Sms = { nomi: "SmsService" };

function modul(nomi, sozlama) {
  return {
    nomi, providers: [], imports: [], exports: [], ...sozlama,
  };
}

function tekshirModul(m) {
  const bor = new Set(m.providers);
  for (const boshqa of m.imports) {
    for (const p of boshqa.exports) bor.add(p);
  }
  return m.exports
    .filter((p) => !bor.has(p))
    .map((p) => `${m.nomi}: ${p.nomi} eksport qilingan, ` +
      "lekin modulda yo'q");
}

function takrorlar(modullar) {
  const qayerda = new Map();
  for (const m of modullar) {
    for (const p of m.providers) {
      qayerda.set(p, [...(qayerda.get(p) ?? []), m.nomi]);
    }
  }
  return [...qayerda]
    .filter(([, nomlar]) => nomlar.length > 1)
    .map(([p, nomlar]) =>
      `${p.nomi} ikki marta yaratiladi: ${nomlar.join(", ")}`);
}

const SoatM = modul("SoatModule", {
  providers: [Soat], exports: [Soat],
});
const Notogri = modul("NotogriModule", { exports: [Sms] });
const Hisobot = modul("HisobotModule", { providers: [Soat] });
const Qayta = modul("UmumiyModule", {
  imports: [SoatM], exports: [Soat],
});

const hamma = [SoatM, Notogri, Hisobot, Qayta];
for (const m of hamma) {
  for (const x of tekshirModul(m)) console.log(x);
}
for (const x of takrorlar(hamma)) console.log(x);

Konsolda:

text
NotogriModule: SmsService eksport qilingan, lekin modulda yo'q
SoatService ikki marta yaratiladi: SoatModule, HisobotModule

UmumiyModule xato bermadi: u Soat ni o'zi yaratmaydi, import qilgan modulning eksportidan qayta beradi. Birinchi xato Nest'ning Nest cannot export a provider/module… xatosiga mos — Nest uni ishga tushishda o'zi beradi. Ikkinchisini esa Nest bermaydi — u jim ikki nusxa yaratadi. Shunday tekshiruvni katta loyihada test sifatida yozish mumkin: Nest'ning DiscoveryService modullar ro'yxatini beradi (Reflector va DiscoveryService).

10. Loyiha qadami: modul qutilari

Bu va keyingi uch dars bitta katta qadamning qismlari: butun v1 API Nest'ga o'tadi. Har darsda bitta qismni qo'shamiz, commit esa Bootstrap: main.ts darsida bitta bo'ladi. Bu darsda — modul fayllari. Ichini keyingi darslar to'ldiradi.

src/vazifalar/vazifalar.module.ts:

ts
// vazifalar.module.ts — vazifalar xususiyat moduli:
// controller'lar + xizmat
import { Module } from "@nestjs/common";

@Module({
  // controllers — keyingi darsda, providers va exports — undan keyin
})
export class VazifalarModule {}

src/eksport/eksport.module.ts:

ts
// eksport.module.ts — eksport xususiyat moduli
import { Module } from "@nestjs/common";

@Module({})
export class EksportModule {}

src/app.module.ts — ikkala modul daraxtga ulanadi:

ts
// app.module.ts — ildiz modul: ilova shu moduldan boshlab
// yig'iladi (imports daraxti)
import { Module } from "@nestjs/common";
import { SalomController } from "./salom.controller.ts";
import { VazifalarModule } from "./vazifalar/vazifalar.module.ts";
import { EksportModule } from "./eksport/eksport.module.ts";

@Module({
  imports: [VazifalarModule, EksportModule],
  controllers: [SalomController],
})
export class AppModule {}

Fayl nomlari Nest uslubida (*.module.ts), mavzu so'zi o'zbekcha. Eski vazifalar/marshrut.ts, xizmat.ts va eksport/eksport.ts hali joyida — Express API ular bilan ishlashda davom etadi.

Tekshiruv:

bash
npm run tip
npm run lint
npm test
npm run build
npm start

Biz kanon nusxasida shu o'zgarish bilan yurgizdik: tip 0 xato, lint 0, Vitest 2/2, build o'tdi, ishga tushish logi — «Ishga tushish logida modullar» bo'limidagi. GET /salom hali {"xabar":"Salom, Nest!"} qaytaradi.

Ko'chish tugagach bu fayllarning yakuniy ko'rinishi (kanon): VazifalarModule — controllers: [VazifalarController, TeglarController], providers: [VazifalarService], exports: [VazifalarService]; EksportModule — EksportController va EksportService; AppModule — forRoot bilan, «Dinamik modul — bir qarash» bo'limidagi ko'rinishda. Hali commit qilmang.

11. Real ishda

  • Katta loyihalarda modullar soni o'nlab: AuthModule, UsersModule, PaymentsModule, NotificationsModule. Yangi dasturchi app.module.ts ni ochib, butun tizim xaritasini ko'radi.
  • Kod review'da eng ko'p savol: "bu provider nega ikki modulning providers ida?" va "nega bu modul @Global?".
  • Modular monolit — modullar chegarasini qat'iy saqlab, keyinchalik ularni alohida xizmatga ajratish mumkin bo'lgan arxitektura. Nest modullari bunga tayyor asos (Modular monolit va hexagonal arxitektura).
  • Intervyu: "Nest'da provider boshqa modulda qanday ishlatiladi?", "@Global qachon kerak va nega kam ishlatish kerak?", "Shared module nima?".

Xulosa

  • @Module — to'rt maydon: controllers, providers, imports, exports; klassning o'zi bo'sh tashuvchi.
  • Provider sukut bo'yicha faqat o'z modulida; boshqa modulga — egasida exports, ishlatuvchida imports.
  • Import o'tkazuvchan emas; qayta eksport bilan bir nechta modulni bitta qilib berish mumkin.
  • Modul import qilinganda nusxa ko'chmaydi — shared; ikki providers esa jim ikki nusxa beradi.
  • @Global — import qilmasdan ko'rinadi, lekin daraxtga bir marta ulanadi; kam ishlating.
  • ESM import — klass kodi uchun, Nest imports — klass nusxasi uchun.

Keyingi dars: Controller'lar va route dekoratorlari — vazifalar-api ning marshrutlarini @Controller, @Get, @Param, @Body bilan yozamiz va @Res() tuzog'ini ko'ramiz.

Manbalar

  • NestJS hujjati — Modules (shared, global, re-export, dynamic): https://docs.nestjs.com/modules
  • NestJS hujjati — Common errors ("Cannot resolve dependency"): https://docs.nestjs.com/faq/common-errors
  • Kanon (IlmHamroh, 2026-10-27): vazifalar-api modul tuzilmasi, NestJS 12.2.0. Modul tajribalari (eksportsiz, eksport, @Global, qayta eksport, ikki providers, noto'g'ri eksport) va vazifalar-api ishga tushish logi — biz ishga tushirdik (2026-11-01, NestJS 12.2.0, Node 24.21.0).
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
NestJS modullari: @Module, imports, providers, exports va @Global — IlmHamroh