Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. createApplicationContext
- 2.1 Ikki kirish nuqtasi
- 2.2 JS modelida
- 3. vazifalar-api ning buyrug'i
- 3.1 src/buyruq.ts
- 3.2 Chiqish kodlari
- 4. Natija: biz ishga tushirdik
- 5. ctx.close() nega shart
- 6. Topilma: stdout iflos
- 7. Nest CLI monorepo
- 7.1 nest g app va nest g lib
- 7.2 nest-cli.json
- 7.3 vazifalar-api ga monorepo kerakmi?
- 8. nest-commander — buyruqlar ko'p bo'lsa
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): argumentlarni tahlil qilish
- 3-mashq (qiyin): ikki oqimli chiqish
- 11. Loyiha qadami: npm run buyruq
- 12. Real ishda
- Xulosa
- Manbalar
NestJS standalone ilova: createApplicationContext, CLI buyruqlar va monorepo
Qisqacha: Nest ilovasi faqat HTTP server emas.
NestFactory.createApplicationContext(AppModule)o'sha modullar, DI, config va lifecycle hook'larni ko'taradi, lekin controller, middleware va port yo'q. Shu bilan terminal buyrug'i, cron skripti yoki fon ishchisi yoziladi:ctx.get(VazifalarService)— tayyor xizmat. Ish tugagachawait ctx.close()shart: aks holda pool va taymerlar jarayonni tirik ushlab turadi. Bir repoda bir nechta Nest ilovasi kerak bo'lsa — Nest CLI monorepo (apps/,libs/).
Bu darsda
- HTTP ilova va standalone kontekst farqini tushuntirasiz.
vazifalar-apiganpm run buyruq -- teglar | eksportbuyrug'ini qo'shasiz.ctx.close()unutilsa nima bo'lishini haqiqiy tajribada ko'rasiz.- Chiqish kodlari (64, 78) va stdout tozaligi qoidasini bilasiz — kanonda topilgan kamchilik bilan.
nest g appvanest g libmonorepo tuzilmasini qanday yaratishini o'qiysiz.
Oldin bilishingiz kerak: Node'da CLI dastur anatomiyasi, Commander bilan buyruqlar tizimi, Lifecycle hook'lar, Dynamic modullar, Rejalashtirish: @nestjs/schedule.
O'tgan darsda HTTP qatlamini almashtirib ko'rdik — Express yoki Fastify. Bugun undan ham ko'proq qilamiz: HTTP qatlamini butunlay olib tashlaymiz. Qolgani — modullar, xizmatlar, ombor — o'z joyida.
1. Nega bu kerak?
Har backend'da HTTP'siz ishlar bor:
- Eksport: "hamma vazifalarni CSV qilib ber" — administrator terminaldan.
- Bir martalik skript: eski ma'lumotni tuzatish, hisobot chiqarish, test ma'lumotini to'ldirish.
- Cron: server cron'i har kecha soat 3 da bir buyruqni ishga tushiradi.
- Fon ishchisi (worker): navbatdan ish olib bajaradigan alohida jarayon.
Bularning hammasiga ilovaning o'sha qismlari kerak: config tekshiruvi, ombor (Postgres pool'i yoki fayl), VazifalarService qoidalari. Ularni qayta yozish — ikki joyda bir xil mantiq degani. Express'da odatda omborYarat(config) ni qo'lda chaqirib, kerakli obyektlarni yig'ib chiqardik.
24-qismda CLI dastur yozishni o'rgandik (Node'da CLI dastur anatomiyasi): argumentlar, chiqish kodlari, stdout/stderr. Endi shu bilimni Nest DI bilan birlashtiramiz.
O'xshatish: restoran oshxonasi. Odatda buyurtma ofitsiant (HTTP) orqali keladi. Lekin ertalab oshpaz zal ochilmasdan oldin xamir tayyorlaydi — o'sha oshxona, o'sha jihozlar, o'sha retseptlar, faqat mijoz yo'q. Standalone kontekst — zal yopiq oshxona.
2. createApplicationContext
2.1 Ikki kirish nuqtasi
flowchart TB
M["main.ts: NestFactory.create"] --> A["AppModule.forRoot()"]
B["buyruq.ts: createApplicationContext"] --> A
A --> P["provider'lar: config, ombor, VazifalarService"]
A --> L["lifecycle: onModuleInit / onModuleDestroy"]
M --> H["HTTP: controller, middleware, port"]
B -. "yo'q" .-> HIkki fayl bitta modul daraxtini ishlatadi. Farq faqat pastki o'ng burchakda: main.ts HTTP qatlamini ham quradi, buyruq.ts — yo'q.
NestFactory.create |
createApplicationContext |
|
|---|---|---|
| Provider'lar, DI | bor | bor |
| Config, lifecycle hook'lar | bor | bor |
@Interval, @Cron |
ishlaydi | ishlaydi |
| Controller, middleware, guard | bor | yo'q |
| HTTP server, port | bor | yo'q |
| Natija | INestApplication |
INestApplicationContext |
@Interval va @Cron kontekstda ham ishlaydi — bu muhim, quyida ko'ramiz. ctx.get(Klass) — kerakli provider'ni beradi, xuddi testdagi m.get(...) kabi (NestJS unit testlari).
2.2 JS modelida
Kichik model (brauzerda ishlaydi). Xizmat ishga tushganda "taymer" ochadi, yopilganda — yopadi:
// Bitta modul — ikki kirish nuqtasi: HTTP ilova va HTTP'siz kontekst
const ochiqTaymerlar = new Set();
class EksportXizmati {
onModuleInit() {
// @Interval o'xshashi: har daqiqa eski ishlarni tozalash
this.t = { nomi: "eksport-tozalash" };
ochiqTaymerlar.add(this.t);
}
onModuleDestroy() {
ochiqTaymerlar.delete(this.t);
}
teglar() {
return [{ teg: "o'qish", soni: 2 }, { teg: "bozor", soni: 1 }];
}
}
function kontekstYarat({ http }) {
const xizmat = new EksportXizmati();
xizmat.onModuleInit();
return {
http: http ? "port ochildi" : "HTTP yo'q",
get: () => xizmat,
close: () => xizmat.onModuleDestroy(),
};
}
const ctx = kontekstYarat({ http: false });
console.log(ctx.http, ctx.get().teglar().length, "ta teg");
console.log("close'dan oldin ochiq:", ochiqTaymerlar.size);
ctx.close();
console.log("close'dan keyin ochiq:", ochiqTaymerlar.size);
const unutilgan = kontekstYarat({ http: false });
unutilgan.get().teglar();
console.log("close unutildi — ochiq:", ochiqTaymerlar.size);Konsolda:
HTTP yo'q 2 ta teg
close'dan oldin ochiq: 1
close'dan keyin ochiq: 0
close unutildi — ochiq: 1Oxirgi qatorga qarang: ish tugadi, lekin taymer ochiq qoldi. Node'da ochiq taymer yoki ulanish bo'lsa — jarayon tugamaydi (Event loop fazalari chuqur). Model soddalashtirilgan: haqiqiy Nest'da onModuleDestroy va onApplicationShutdown hook'lari ctx.close() da, hamma modul bo'ylab chaqiriladi (Lifecycle hook'lar).
Tekshirib ko'ring: Standalone kontekstda
ctx.get(VazifalarController)nima qaytaradi? Controller'ning metodini chaqirib, HTTP marshrutini "sinab" bo'ladimi?
Javob
Tekshirdik: nusxa qaytadi — controller: VazifalarController. Nest controller klassini ham yaratadi, chunki u modulda yozilgan. Lekin marshrut, middleware, guard, pipe va filtr yo'q: metodni chaqirsangiz, oddiy funksiya chaqiruvi bo'ladi, HTTP so'rov emas. Bu "marshrut sinovi" emas — validatsiya va xato filtri ishlamaydi. Standalone kontekstdan faqat provider'larni (xizmatlarni) oling. Biznes mantiq xizmatda bo'lishining yana bir sababi shu.
3. vazifalar-api ning buyrug'i
3.1 src/buyruq.ts
Kanon fayli, telefon uchun bo'lingan (ma'no o'zgarmagan, izohlar qisqartirilgan):
// buyruq.ts — standalone ilova (HTTP'siz): o'sha modullar, o'sha DI,
// o'sha config; terminal buyrug'i yoki cron skripti uchun.
// npm run buyruq -- teglar — teglar va soni (jadval)
// npm run buyruq -- eksport [csv|json] > fayl
// — hamma vazifalar stdout'ga (EksportService mantiqi)
// NestFactory.createApplicationContext — controller'lar, HTTP server,
// middleware YO'Q; provider'lar va lifecycle hook'lar BOR
import "reflect-metadata";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module.ts";
import { XotiraIdempotentlikOmbori } from "./umumiy/idempotentlik.ts";
import { VazifalarService } from "./vazifalar/vazifalar.service.ts";
import { csvQator } from "./umumiy/csv.ts";
import { ConfigXatosi } from "./config.ts";
const [buyruq, format = "csv"] = process.argv.slice(2);
if (buyruq !== "teglar" && buyruq !== "eksport") {
console.error(
"Foydalanish: npm run buyruq -- teglar | eksport [csv|json]",
);
process.exit(64); // EX_USAGE
}
let ctx;
try {
ctx = await NestFactory.createApplicationContext(
AppModule.forRoot({
idempotentlikOmbori: new XotiraIdempotentlikOmbori(),
}),
{ logger: ["error", "warn"], abortOnError: false },
);
} catch (x) {
if (!(x instanceof ConfigXatosi)) throw x;
console.error(x.message);
process.exit(78);
}
try {
const xizmat = ctx.get(VazifalarService);
if (buyruq === "teglar") {
const teglar = await xizmat.teglar();
console.table(teglar.map(({ teg, soni }) => ({ teg, soni })));
} else {
// kursor bilan bo'lak-bo'lak (100 tadan) — katta ro'yxat
// xotiraga birdan yuklanmaydi
const ustunlar = [
"id", "matn", "bajarildi", "teglar", "yaratildi", "yangilandi",
] as const;
if (format === "csv") process.stdout.write(csvQator(ustunlar));
let keyin: (string | number | boolean)[] | undefined;
let birinchi = true;
if (format === "json") process.stdout.write("[");
for (;;) {
const n = await xizmat.royxat({
filtr: {},
saralash: [],
limit: 100,
sahifa: keyin === undefined
? { tur: "kursor" }
: { tur: "kursor", keyin },
});
for (const v of n.vazifalar) {
process.stdout.write(
format === "csv"
? csvQator([v.id, v.matn, v.bajarildi,
v.teglar.join(" "), v.yaratildi, v.yangilandi])
: (birinchi ? "" : ",") + JSON.stringify(v),
);
birinchi = false;
}
if (!n.borKeyingi || n.keyingi === undefined) break;
keyin = n.keyingi;
}
if (format === "json") process.stdout.write("]\n");
}
} finally {
// @Interval taymerlari va pool — yopilmasa jarayon tugamaydi
await ctx.close();
}Qismma-qism:
- Argumentlar —
process.argv.slice(2), 24-qismdagidek (Node'da CLI dastur anatomiyasi). Noma'lum buyruq — foydalanish matni stderr ga va chiqish kodi 64. - Kontekst —
AppModule.forRoot(...)— HTTP ilovadagi o'sha dinamik modul (Dynamic modullar).idempotentlikOmbori— modul talab qiladigan, lekin buyruqqa kerak bo'lmagan bog'liqlik; xotiradagisi yetadi. logger: ["error", "warn"]— Nest'ning o'z tizim loglari (InstanceLoader…) chiqmasin, faqat xato va ogohlantirish.abortOnError: false— provider fabrikasi yiqilsa (config xato, baza yo'q), Nestprocess.exit(1)qilmaydi, xatocatchga keladi. Shu yerdaConfigXatosi78 ga aylanadi.- Eksport — kursor bilan 100 tadan (Pagination: offset, cursor, keyset). 10 000 vazifa bo'lsa ham xotirada bir vaqtda 100 tasi turadi.
csvQator— 25-qismdagi eksport funksiyasi. finally+ctx.close()— xato bo'lsa ham kontekst yopiladi (try, catch, finally).
3.2 Chiqish kodlari
64 va 78 tasodifiy raqam emas. Ular Unix'dagi sysexits.h kelishuvidan:
| Kod | Nomi | Ma'nosi |
|---|---|---|
| 0 | — | muvaffaqiyat |
| 1 | — | umumiy xato (ushlanmagan istisno) |
| 64 | EX_USAGE |
buyruq noto'g'ri ishlatildi |
| 78 | EX_CONFIG |
sozlama xato |
Cron yoki CI skripti kodga qarab qaror qiladi: 64 — dasturchi xatosi (buyruq satri), 78 — muhit xatosi (.env). 24-qismda chiqish kodlari umumiy ma'noda o'tilgandi; bu yerda aniq kelishuvni ishlatamiz.
4. Natija: biz ishga tushirdik
Lokal nusxada ilovani SAQLASH=fayl bilan ko'tarib, curl bilan to'rtta vazifa qo'shdik va bittasini bajarilgan qildik. Keyin serverni to'xtatib, buyruqni yurgizdik (NestJS 12.2.0, Node 24.21.0; sanalar — o'sha paytdagi):
npm run build
SAQLASH=fayl npm run buyruq -- teglar┌─────────┬──────────┬──────┐
│ (index) │ teg │ soni │
├─────────┼──────────┼──────┤
│ 0 │ "o'qish" │ 2 │
│ 1 │ 'bozor' │ 1 │
│ 2 │ 'oila' │ 1 │
└─────────┴──────────┴──────┘console.table satrlarni qo'shtirnoq bilan chiqaradi: apostrofli satr — qo'sh qo'shtirnoqda, qolgani — bittalikda. Bu Node'ning util.inspect qoidasi, xato emas. Jarayon 1.8 s da tugadi. Kanon Postgres va Prisma bilan o'lchaganda — 2.15 s.
SAQLASH=fayl npm run buyruq -- eksport csvid,matn,bajarildi,teglar,yaratildi,yangilandi
1,Non olish #bozor,true,bozor,2026-11-02T06:30:41.049Z,2026-11-02T06:30:41.350Z
2,Postgres repository yozish #o'qish,false,o'qish,2026-11-02T06:30:41.141Z,2026-11-02T06:30:41.141Z
3,Onamga dori olib borish #oila,false,oila,2026-11-02T06:30:41.215Z,2026-11-02T06:30:41.215Z
4,SQL JOIN mashqlari #o'qish,false,o'qish,2026-11-02T06:30:41.292Z,2026-11-02T06:30:41.292Z
{"daraja":"info","time":"2026-11-02T06:30:54.204Z","ilova":"vazifalar-api","msg":"ombor ulanishlari yopildi"}Oxirgi qatorga qarang — u CSV emas. Bu haqda «Topilma: stdout iflos» bo'limida.
Noto'g'ri buyruq va noto'g'ri sozlama:
npm run buyruq -- xyz; echo "exit $?"
SAQLASH=postgres npm run buyruq -- teglar; echo "exit $?"Foydalanish: npm run buyruq -- teglar | eksport [csv|json]
exit 64
[Nest] 9344 - 02/11/2026, 11:36:27 ERROR [ExceptionHandler] ConfigXatosi: Sozlama noto'g'ri:
✖ SAQLASH=postgres/prisma uchun DATABASE_URL majburiy
→ at DATABASE_URL
at configYukla (file:///C:/Users/…/vazifalar-api/dist/config.js:74:15)
… (Nest ichki stack qatorlari)
Sozlama noto'g'ri:
✖ SAQLASH=postgres/prisma uchun DATABASE_URL majburiy
→ at DATABASE_URL
exit 78Yo'lni qisqartirdik va Nest ichidagi stack qatorlarini olib tashladik. Kod to'g'ri — 78. Lekin xabar ikki marta chiqdi: avval Nest'ning ExceptionHandler logi (stack bilan), keyin bizning console.error. abortOnError: false jarayonni o'ldirmaydi, lekin Nest xatoni baribir logga yozadi. Toza chiqish kerak bo'lsa — logger: false yoki ["warn"]; u holda boshqa xatolar ham yashirinadi. Kanon ikki marta chiqishni qabul qilgan.
5. ctx.close() nega shart
Kanon izohida: "@Interval taymerlari va pool — yopilmasa jarayon tugamaydi". Tekshirdik: buyruq.js nusxasidan await ctx.close() qatorini olib tashlab, 15 soniya chegarasi bilan yurgizdik:
timeout 15 node dist/buyruq-yopiqsiz.js teglar; echo "exit $?"┌─────────┬──────────┬──────┐
│ (index) │ teg │ soni │
├─────────┼──────────┼──────┤
│ 0 │ "o'qish" │ 2 │
│ 1 │ 'bozor' │ 1 │
│ 2 │ 'oila' │ 1 │
└─────────┴──────────┴──────┘
exit 124Jadval chiqdi — ish bajarildi. Lekin jarayon tugamadi: 15 soniyadan keyin timeout uni o'ldirdi, 124 — timeout ning "vaqt tugadi" kodi. Sabab — EksportService dagi @Interval("eksport-tozalash", 60_000) (Rejalashtirish): har daqiqa eski eksport ishlarini tozalaydigan taymer kontekstda ham ishga tushdi. Postgres rejimida unga pool ulanishlari qo'shiladi.
Cron'da bunday buyruq har kecha yangi "osilgan" jarayon qoldiradi — bir haftadan keyin serverda yettita. process.exit(0) bilan majburan chiqish ham yechim emas: pool yopilmaydi, yozilayotgan fayl oxirigacha yozilmasligi mumkin.
Diqqat: Standalone kontekstni har doim
try/finallyichidaawait ctx.close()bilan yoping. Xato bo'lsa ham.
6. Topilma: stdout iflos
Eksport chiqishining oxirgi qatori — JSON log: "ombor ulanishlari yopildi". Uni OmborModule ning yopish hook'i yozadi (ctx.close() paytida). vazifalar-api ning logi Pino orqali stdout ga yoziladi — 25-qismdagi 12-factor qoidasi (Logging asoslari). HTTP serverda bu to'g'ri. Buyruqda esa stdout — ma'lumot kanali.
Tekshirdik:
npm run buyruq -- eksport csv > vazifalar.csv
tail -1 vazifalar.csv{"daraja":"info","time":"2026-11-02T06:31:10.924Z","ilova":"vazifalar-api","msg":"ombor ulanishlari yopildi"}CSV faylning oxirgi qatori — log. Excel yoki boshqa dastur uni "noto'g'ri qator" deb ko'radi, JSON eksportda esa fayl umuman JSON bo'lmay qoladi. Kanon o'lchovi bu chiqishni | head -4 bilan ko'rsatgani uchun xato ko'rinmay qolgan.
Qoida (24-qism CLI darsidan): stdout — ma'lumot, stderr — xabarlar va log. Hozircha ishlaydigan yo'l — buyruqni yuqoriroq log darajasi bilan yurgizish:
LOG_LEVEL=warn npm run buyruq -- eksport csv > vazifalar.csvLOG_LEVEL=warn bilan info loglar chiqmaydi va fayl toza bo'ladi (tekshirdik). To'liq tuzatish — buyruq rejimida logni stderr'ga yo'naltirish. Uni 3-mashqda modelda qilasiz. Bu kamchilik kanonda tuzatilmagan — loyihangizda LOG_LEVEL=warn ni buyruq skriptiga qo'shib qo'yishingiz mumkin.
Tekshirib ko'ring: Nega bu xato HTTP serverda muammo emas, buyruqda esa muammo?
Javob
HTTP serverda ma'lumot tarmoq orqali (javob tanasi) ketadi, stdout faqat log uchun. Buyruqda esa stdout'ning o'zi ma'lumot: > fayl.csv uni faylga yozadi. Logni ham stdout'ga yozsak, ikki oqim aralashadi.
7. Nest CLI monorepo
7.1 nest g app va nest g lib
Bir repoda bir nechta Nest ilovasi kerak bo'lishi mumkin: API va alohida ishchi, yoki API va admin panel backend'i. Ular umumiy kodni (modellar, config, ombor) ulashadi. Nest CLI buni monorepo rejimi deb ataydi.
Sinab ko'rdik: nest new mono, keyin ikki buyruq (biz ishga tushirdik, @nestjs/cli 12.0.9):
npx @nestjs/cli@12.0.9 generate app ishchi
npx @nestjs/cli@12.0.9 generate library umumiyBirinchi buyruq chiqishi (qisqartirilgan):
Added @rspack/core, fork-ts-checker-webpack-plugin, tsconfig-paths-webpack-plugin and webpack-node-externals for the rspack builder; running "npm install"
DELETE src
DELETE test
CREATE apps/mono/src/app.module.ts (265 bytes)
CREATE apps/mono/src/main.ts (245 bytes)
CREATE apps/ishchi/src/main.ts (254 bytes)
CREATE apps/ishchi/src/ishchi.module.ts (286 bytes)
CREATE apps/ishchi/test/jest-e2e.json (192 bytes)
UPDATE tsconfig.json (775 bytes)
UPDATE package.json (1584 bytes)
UPDATE nest-cli.json (787 bytes)Uchta muhim narsa:
- Loyiha qayta tuzildi:
src/vatest/o'chirildi, eski ilovaapps/mono/ga ko'chdi. Bu buyruq mavjud loyihani o'zgartiradi — avval commit qiling. - Builder almashdi: monorepo uchun CLI
rspackbuilder'ini (webpack'ga mos, tezroq yig'uvchi) qo'ydi va to'rt paket o'rnatdi.nest build ishchi— 2.75 s da yig'ildi (Rspack 2.3.0 compiled successfully). - Shablon qoldiqlari: yangi ilovaga
test/jest-e2e.jsonkeldi, lekin loyiha Vitest'da.npm run lintesa eski yo'llarni qidiradi:oxlint --type-aware src/ test/→ "No files found to lint". Ikkalasini qo'lda tuzatish kerak.
Kutubxona buyrug'i libs/umumiy/ yaratdi va tsconfig.json ga yo'l taxallusini qo'shdi:
"paths": {
"@app/umumiy": ["./libs/umumiy/src/index.ts"],
"@app/umumiy/*": ["./libs/umumiy/src/*"]
}Endi har ilova import { UmumiyModule } from "@app/umumiy" yozadi. Builder bu taxallusni yig'ish paytida haqiqiy yo'lga aylantiradi.
7.2 nest-cli.json
{
"monorepo": true,
"root": "apps/mono",
"projects": {
"ishchi": {
"type": "application",
"root": "apps/ishchi",
"entryFile": "main",
"sourceRoot": "apps/ishchi/src"
}
}
}Qisqartirilgan: haqiqiy faylda mono loyihasi, umumiy kutubxonasi va har biriga tsConfigPath ham bor. nest build ishchi, nest start ishchi — loyiha nomi bilan. Nomsiz — root dagi ilova.
7.3 vazifalar-api ga monorepo kerakmi?
Kanon monorepo'ni sinamadi — loyiha uchun kerak emas. vazifalar-api da bitta ilova va bir nechta kirish nuqtasi bor: main.ts (HTTP), buyruq.ts (CLI). 29-qism integratsiyalari Nest'ga ko'chganda yana bittasi qo'shiladi — navbat ishchisi (Navbatlar: @nestjs/bullmq darsida). Hammasi bitta src/ va bitta package.json da, dist/ dan alohida fayl sifatida ishga tushadi. Monorepo qachon foydali:
| Holat | Yetarli yechim |
|---|---|
| bitta ilova + buyruqlar + ishchi | bitta loyiha, bir nechta kirish fayli |
| bir nechta mustaqil deploy qilinadigan ilova, umumiy kod | Nest CLI monorepo |
| Nest + frontend + boshqa paketlar birga | npm workspaces + Turborepo/Nx |
Oxirgi qator — IlmHamroh platformasining o'zi: Next.js va NestJS bitta repoda, npm workspaces va Turborepo bilan. Bu boshqa daraja — Nest CLI monorepo faqat Nest ilovalari uchun.
8. nest-commander — buyruqlar ko'p bo'lsa
buyruq.ts da ikkita buyruq bor va argumentlar process.argv bilan qo'lda o'qiladi. O'nta buyruq, bayroqlar (--format, --dan), yordam matni kerak bo'lsa — 24-qismdagi Commander kerak bo'ladi (Commander bilan buyruqlar tizimi). Nest uchun uning o'rami bor — nest-commander paketi: har buyruq — @Command({ name }) dekoratorli klass, CommandRunner dan meros oladi, bog'liqliklarni constructor orqali DI'dan oladi. Ishga tushirish — CommandFactory.run(AppModule).
Halol aytamiz: nest-commander'ni kanon ham, biz ham sinamadik. Ikki buyruq uchun qo'lda tahlil yetarli va bog'liqlik qo'shilmaydi. Buyruqlar ko'paysa — paketning rasmiy hujjatidan boshlang.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Buyruq ishini qildi, lekin tugamaydi | ctx.close() yo'q — taymer, pool |
try/finally + await ctx.close() |
| Eksport fayli oxirida JSON qator | log stdout'ga | log darajasi yoki log — stderr'ga |
| Config xatosida jim exit 1 | abortOnError sukut (true) |
abortOnError: false + catch |
nest g app dan keyin src/ yo'q |
monorepo rejimi loyihani ko'chirdi | avval commit; yangi yo'llarga moslang |
npm run lint "No files found" |
lint skripti eski src/ ga qaraydi |
apps/ libs/ ga o'zgartiring |
10. Mashqlar
Mashqlar kurs/mashqlar/30/52-standalone/ da.
1-mashq (oson): to'ldiring
- HTTP'siz Nest konteksti:
NestFactory.(AppModule). - Tayyor xizmatni olish:
ctx.(VazifalarService). - Noto'g'ri buyruq — chiqish kodi [:64], noto'g'ri sozlama — [:78].
- Ma'lumot qaysi oqimga yoziladi: [:stdout]; xabar va log — [:stderr].
Yechim
- createApplicationContext — provider'lar va lifecycle bor, HTTP yo'q.
- get — DI konteynerdagi nusxa.
- 64 (
EX_USAGE) va 78 (EX_CONFIG) —sysexits.hkelishuvi. - stdout —
> faylbilan faylga ketadi; stderr — ekranda qoladi.
2-mashq (o'rta): argumentlarni tahlil qilish
tahlil(argv) funksiyasini yozing. U { kod, buyruq, format, xabar } qaytarsin: teglar — kod: 0; eksport — format sukut "csv", faqat csv yoki json ruxsat; boshqa hamma holat — kod: 64 va foydalanish xabari. To'rtta holatni sinang. Ishora: massiv destructuring sukut qiymat bilan (Massiv destructuring).
Yechim
const YORDAM = "Foydalanish: buyruq teglar | eksport [csv|json]";
function tahlil(argv) {
const [buyruq, format = "csv"] = argv;
if (buyruq === "teglar") return { kod: 0, buyruq };
if (buyruq === "eksport" && ["csv", "json"].includes(format)) {
return { kod: 0, buyruq, format };
}
return { kod: 64, xabar: YORDAM };
}
for (const a of [["teglar"], ["eksport"], ["eksport", "xml"], []]) {
console.log(JSON.stringify(a), "→", JSON.stringify(tahlil(a)));
}Konsolda:
["teglar"] → {"kod":0,"buyruq":"teglar"}
["eksport"] → {"kod":0,"buyruq":"eksport","format":"csv"}
["eksport","xml"] → {"kod":64,"xabar":"Foydalanish: buyruq teglar | eksport [csv|json]"}
[] → {"kod":64,"xabar":"Foydalanish: buyruq teglar | eksport [csv|json]"}Kanon fayli xml ni tekshirmaydi: eksport xml JSON yo'liga ham, CSV yo'liga ham to'liq tushmaydi — ma'lumot qatorlari JSON shaklida chiqadi, lekin [/] siz. Bu mashqdagi tekshiruv shu kichik bo'shliqni yopadi.
3-mashq (qiyin): ikki oqimli chiqish
Paketsiz model. buyruqYurgiz(oqimlar) — oqimlar da stdout va stderr massivlari bor. Funksiya CSV sarlavhasi va ikki qatorni stdout ga, "ombor ulanishlari yopildi" logini stderr ga yozsin. Keyin stdout ni "faylga" (bitta satrga \n bilan) yig'ing va qatorlar sonini hamda oxirgi qatorni chiqaring. Avval xato variantni (log ham stdout ga), keyin to'g'risini ko'rsating.
Yechim
function buyruqYurgiz(oqimlar, logOqimi) {
oqimlar.stdout.push("id,matn");
oqimlar.stdout.push("1,Non olish");
oqimlar.stdout.push("2,Sut olish");
const log = JSON.stringify({ msg: "ombor ulanishlari yopildi" });
oqimlar[logOqimi].push(log);
}
for (const logOqimi of ["stdout", "stderr"]) {
const oqimlar = { stdout: [], stderr: [] };
buyruqYurgiz(oqimlar, logOqimi);
const fayl = oqimlar.stdout.join("\n");
const qatorlar = fayl.split("\n");
console.log(`log → ${logOqimi}: faylda ${qatorlar.length} qator,`,
`oxirgisi: ${qatorlar.at(-1)}`);
}Konsolda:
log → stdout: faylda 4 qator, oxirgisi: {"msg":"ombor ulanishlari yopildi"}
log → stderr: faylda 3 qator, oxirgisi: 2,Sut olishHaqiqiy loyihada buning bir yo'li — buyruq rejimida Pino'ni pino.destination(2) (2 — stderr fayl deskriptori) bilan yaratish. Logger'ni AppModule.forRoot({ log }) orqali berish mumkin — modul shu imkoniyatni testlar uchun allaqachon ochgan.
11. Loyiha qadami: npm run buyruq
Bu dars NestJS prodakshnda va NestJS versiyalarini yangilash bilan bitta qadam: vazifalar-api 2.0.0 versiyasiga tayyorlanadi. Commit — uchinchi darsda.
- Yangi fayl
src/buyruq.ts— «src/buyruq.ts» bo'limidagi to'liq matn. package.jsondagiscriptsga:
"buyruq": "node --env-file-if-exists=.env dist/buyruq.js"Skript dist/ dan ishlaydi — avval npm run build. --env-file-if-exists — 24-qismdagi Node bayrog'i (--watch, --env-file va node --run): .env bo'lsa o'qiydi, bo'lmasa jim davom etadi. nest build src/ dagi hamma faylni yig'adi, buyruq.ts uchun alohida sozlama kerak emas.
Tekshiruv:
npm run tip
npm run lint
npm run build
npm run buyruq -- teglar
npm run buyruq -- xyz; echo "exit $?"Formatlangan buyruq.ts ni loyiha nusxasida tekshirdik: tsc --noEmit 0 xato, eslint 0, build o'tdi, «Natija» bo'limidagi chiqishlar shu build'dan. Hali commit qilmang.
12. Real ishda
- Migratsiya va seed buyruqlari ko'p Nest loyihalarida standalone kontekst bilan yoziladi:
npm run seed,npm run hisobot:oylik. Afzalligi — config va ombor bitta joyda. - Kubernetes CronJob / server cron'i buyruqni ishga tushiradi va chiqish kodiga qaraydi. Osilib qolgan jarayon — CronJob'ning keyingi ishga tushishini to'sadi.
- Ishchi jarayon: navbat ishchisi ham standalone kontekst bo'lishi mumkin — HTTP'siz, faqat navbatni tinglaydi. Bu tanlovni Navbatlar: @nestjs/bullmq darsida o'lchov bilan ko'ramiz.
- Intervyu: "Nest'da HTTP'siz skript qanday yoziladi?", "Nega skript tugamay qoladi?", "Nest monorepo va npm workspaces farqi?".
Xulosa
createApplicationContext— o'sha modullar, DI, config, lifecycle va taymerlar; HTTP, controller va middleware yo'q.ctx.get(Xizmat)— tayyor xizmat; ish tugagachtry/finallydaawait ctx.close()— aks holda@Intervalva pool jarayonni ushlaydi.- CLI qoidalari: stdout — ma'lumot, stderr — log; chiqish kodlari 64 (
EX_USAGE), 78 (EX_CONFIG). - Kanon buyrug'ida log stdout'ga tushadi — eksport fayli oxirida JSON qator; hozircha
LOG_LEVEL=warn. - Nest CLI monorepo (
nest g app/lib) loyihaniapps/,libs/ga ko'chiradi va rspack qo'yadi;vazifalar-apiga kerak emas.
Keyingi dars: NestJS prodakshnda — build (tsc va SWC), prod hajmi, Express va Nest o'lchovi, test qamrovi va check skriptiga build qo'shiladi.
Manbalar
- NestJS hujjati — Standalone applications: https://docs.nestjs.com/standalone-applications
- NestJS hujjati — Workspaces (monorepo, libraries): https://docs.nestjs.com/cli/monorepo
- nest-commander hujjati: https://nest-commander.jaymcdoniel.dev/
- FreeBSD
sysexits(3)— EX_USAGE 64, EX_CONFIG 78: https://man.freebsd.org/cgi/man.cgi?query=sysexits - Kanon (IlmHamroh, 2026-10-28):
src/buyruq.ts, Prisma bilan 2.15 s o'lchovi. Buyruq chiqishlari,ctx.close()siz tajriba, stdout topilmasi vanest g app/lib— biz ishga tushirdik (2026-11-02, NestJS 12.2.0, @nestjs/cli 12.0.9, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!