IlmHamroh
JavaScript Full-stack/29-qism. Backend integratsiyalar: fayl, to'lov, bot, realtime65/69-dars14 daqiqa
Mundarija (24)

Koa, Elysia va AdonisJS: Node freymvorklarining uch xil yo'li

Qisqacha: Koa — Express mualliflarining minimal freymvorki: hamma narsa async (ctx, next) middleware, await next() atrofida "piyoz" bo'lib ishlaydi. Elysia — Bun uchun yozilgan, TypeBox sxemasi bilan tipli va tez freymvork (validatsiya xatosi — 422). AdonisJS — aksincha, "hammasi ichida": ORM, auth, validatsiya, shablonlar va CLI bitta paketda. Uchalasi uch xil falsafa.

Bu darsda

  • Koa'ning piyoz middleware'ini o'zingiz 15 qatorda yozasiz va uning tartibini tushuntirasiz.
  • Koa'da ctx, ctx.throw va xato ushlovchi middleware yozasiz.
  • Elysia'da TypeBox bilan validatsiya qilib, 422 javobini ko'rasiz.
  • AdonisJS, H3 va Nitro qanday freymvorklar ekanini bilasiz.
  • Loyiha uchun freymvork tanlash jadvalidan foydalana olasiz.

Oldin bilishingiz kerak: Hono, Middleware va request lifecycle, Global error handler, Express 5: async xatolar.

Oldingi darsda Hono'ning await next() middleware'ini ko'rdik va "piyoz tartibini keyingi darsda" deb va'da qildik. Bu tartibni birinchi bo'lib Koa ommalashtirgan.

1. Nega bu kerak?

Ish e'lonida "Koa", "AdonisJS" yoki "Elysia" deb yozilgan bo'lishi mumkin. Eski loyihada Koa, yangi startapda Elysia, tayyor MVC kerak bo'lgan jamoada AdonisJS uchraydi. Ularni chuqur o'rganish shart emas. Lekin har birining g'oyasini bilsangiz, kodni bir kunda o'qib keta olasiz.

Uch yondashuv:

Freymvork Falsafasi O'xshatish
Koa minimal yadro, hammasi middleware bo'sh oshxona: plita bor, qolganini o'zingiz olib kelasiz
Elysia Bun uchun, tip va tezlik tez ovqat sexi: bitta taom turi, juda tez
AdonisJS hammasi ichida (batteries-included) to'liq jihozlangan restoran: menyu, xodim, kassa tayyor

Express — o'rtada: kichik yadro, lekin router va JSON ichida.

2. Koa: piyoz middleware

2.1 G'oyani o'zimiz yozamiz

Koa'ning yuragi — koa-compose: middleware massivini bitta funksiyaga aylantiradi. Har middleware next() ni chaqirsa, keyingisi bajariladi; await next() qaytgach, o'zining qolgan qismi ishlaydi. Bu blok brauzerda jonli:

js
function zanjir(middlewarelar) {
  return function (ctx) {
    function bajar(i) {
      const mw = middlewarelar[i];
      if (!mw) return Promise.resolve();
      return Promise.resolve(mw(ctx, () => bajar(i + 1)));
    }
    return bajar(0);
  };
}

const ishla = zanjir([
  async (ctx, next) => {
    ctx.log.push("1 kirdi");
    await next();
    ctx.log.push("1 chiqdi");
  },
  async (ctx, next) => {
    ctx.log.push("2 kirdi");
    await next();
    ctx.log.push("2 chiqdi");
  },
  async (ctx) => {
    ctx.log.push("marshrut");
    ctx.body = { salom: "dunyo" };
  },
]);

const ctx = { log: [] };
await ishla(ctx);
console.log(ctx.log.join(" > "));
console.log(ctx.body);

Konsolda:

text
1 kirdi > 2 kirdi > marshrut > 2 chiqdi > 1 chiqdi
{ salom: 'dunyo' }

So'rov piyozning qatlamlari orqali ichkariga kiradi, javob esa o'sha qatlamlar orqali tashqariga chiqadi. Birinchi kirgan — oxirgi chiqadi.

flowchart LR
  S["so'rov"] --> A["1: kirdi"]
  A --> B["2: kirdi"]
  B --> M["marshrut: ctx.body"]
  M --> B2["2: chiqdi"]
  B2 --> A2["1: chiqdi"]
  A2 --> J["javob"]

Express'da middleware odatda next() dan keyin hech narsa qilmaydi — javob res.json() bilan allaqachon jo'natilgan. Koa'da javob faqat zanjir to'liq tugagach yuboriladi. Shuning uchun tashqi qatlam javobni o'zgartira oladi: vaqtni o'lchash, sarlavha qo'shish, siqish.

2.2 Haqiqiy Koa

bash
npm i koa@3.2.1 @koa/router@15.7.0

Koa'da req va res o'rniga bitta ctx (kontekst): ctx.method, ctx.path, ctx.query, ctx.status, ctx.body. ctx.body ga obyekt bersangiz — JSON, satr bersangiz — matn. Router Koa ichida yo'q — @koa/router alohida paket. Tana o'qish ham yo'q (@koa/bodyparser kerak; kanon tanani for await bilan o'zi o'qigan).

Biz ishga tushirdik (Node 24.21.0, koa 3.2.1, @koa/router 15.7.0):

js
import Koa from "koa";
import Router from "@koa/router";

const app = new Koa();
const r = new Router();

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (e) {
    ctx.status = e.status ?? 500;
    ctx.body = { xato: e.expose ? e.message : "Ichki xato" };
  }
});
app.use(async (ctx, next) => {
  console.log("1 ->", ctx.method, ctx.path);
  await next();
  console.log("1 <-", ctx.status);
});
app.use(async (ctx, next) => {
  console.log("2 ->");
  await next();
  console.log("2 <-");
});

r.get("/v1/salom", (ctx) => {
  console.log("marshrut");
  ctx.body = { salom: "dunyo" };
});
r.get("/v1/xato", (ctx) => ctx.throw(404, "Vazifa topilmadi"));
r.get("/v1/buzuq", () => {
  throw new Error("DB parol: maxfiy");
});
app.use(r.routes());

const server = app.listen(0, "127.0.0.1");
await new Promise((ok) => server.once("listening", ok));
const url = `http://127.0.0.1:${server.address().port}`;
for (const y of ["/v1/salom", "/v1/xato", "/v1/buzuq"]) {
  const res = await fetch(url + y);
  console.log(res.status, await res.text());
}
server.closeAllConnections();
server.close();

Konsolda:

text
1 -> GET /v1/salom
2 ->
marshrut
2 <-
1 <- 200
200 {"salom":"dunyo"}
1 -> GET /v1/xato
2 ->
404 {"xato":"Vazifa topilmadi"}
1 -> GET /v1/buzuq
2 ->
500 {"xato":"Ichki xato"}

Uch narsaga qarang:

  1. Birinchi so'rovda tartib — biz yozgan zanjir bilan aynan bir xil.
  2. Xatoli so'rovlarda 2 <- va 1 <- qatorlari yo'q. Xato await next() dan otildi va qatlamlarni "teshib" eng tashqi try/catch ga yetdi. Shuning uchun xato ushlovchi middleware birinchi ulanadi.
  3. ctx.throw(404, …) yasagan xatoda expose: true — xabarni mijozga ko'rsatsa bo'ladi. Oddiy Error da expose yo'q — biz "Ichki xato" dedik, parol matni chiqmadi. Bu Global error handler dagi qoidaning Koa varianti.

Tekshirib ko'ring: Vaqt o'lchaydigan middleware'ni Koa'da qayerga qo'yasiz — boshigami, oxirigami? Nega?

Javob

Boshiga (xato ushlovchidan keyin). U await next() dan oldin vaqtni oladi va keyin farqni hisoblaydi — ichkaridagi hamma qatlam va marshrut vaqti o'lchanadi. Oxiriga qo'ysangiz, faqat marshrutni o'lchaydi.

2.3 Koa qachon

Koa 3 — 2025-yilda chiqqan yangi katta versiya (npm reliz tarixi; 2026-10-28 holatiga 3.2.1). U async/await ni birinchilardan bo'lib asos qilgan. Bugun Hono ham, Express 5 ham async xatolarni yaxshi ushlaydi, shuning uchun Koa'ning afzalligi kichraydi. Yangi loyihada kamroq tanlanadi, lekin eski kodda ko'p uchraydi.

Kanon o'lchovi: Koa GET'da 14 457 so'rov/s, Express — 10 698, Fastify — 20 514 (bazasiz, bitta mashina).

3. Elysia: Bun uchun freymvork

3.1 G'oya

Elysia — Bun runtime'i uchun yozilgan freymvork (Bun runtime — keyingi darsda). U ham Hono kabi Request/Response ustida, lekin ikki narsaga urg'u beradi: tezlik va TypeScript tiplari. Validatsiya uchun TypeBox ishlatiladi — Fastify darsida ko'rgan JSON Schema yasovchisi, Elysia'da t nomi bilan.

bash
bun add elysia@1.4.30

3.2 Validatsiya va 422

Biz ishga tushirdik (Bun 1.4.3, elysia 1.4.30). app.handle() — Request olib Response qaytaradi, port ochmaydi:

js
import { Elysia, t } from "elysia";

const app = new Elysia()
  .get("/v1/salom", () => ({ salom: "dunyo" }))
  .post(
    "/v1/vazifalar",
    ({ body, set }) => {
      set.status = 201;
      return { id: 1, matn: body.matn.trim() };
    },
    {
      body: t.Object({
        matn: t.String({ minLength: 1, maxLength: 100 }),
      }),
    },
  );

const so = (body) => new Request("http://localhost/v1/vazifalar", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify(body),
});
for (const b of [{ matn: " Non olish " }, { matn: "" }]) {
  const r = await app.handle(so(b));
  const matn = await r.text();
  console.log(r.status, matn.slice(0, 120).replace(/\s+/g, " "));
}

Konsolda (tananing boshi, bo'shliqlarni bittaga qisqartirdik):

text
201 {"id":1,"matn":"Non olish"}
422 { "type": "validation", "on": "body", "property": "/matn", "message": "Expected string length greater or equal t

Diqqat: Elysia validatsiya xatosiga 422 beradi (Unprocessable Content), Express, Fastify va Hono esa 400. Ikkalasi ham to'g'ri: Status kodlarni to'g'ri tanlash darsida ko'rganimizdek, 422 — "sintaksis to'g'ri, mazmun noto'g'ri". Lekin bitta API ichida bir xil bo'lsin — frontend shunga tayanadi.

Javob uchun return yetarli: obyekt — JSON. set.status — status. Marshrut zanjir bilan yoziladi (.get().post()), xuddi Hono'dagi kabi — tiplar zanjir bo'ylab "oqadi". Elysia'ning Eden klienti shu tiplardan Hono'ning hc iga o'xshash tipli klient yasaydi (hujjat bo'yicha, sinalmagan).

3.3 Elysia Node'da

Kutilmagan topilma: biz o'sha faylni Node 24.21.0 da ishga tushirdik — app.handle() Node'da ham xuddi shu natijani berdi. Lekin .listen() Bun'ning serveriga tayanadi. Node'da server ochish uchun hujjat @elysia/node adapterini ko'rsatadi (new Elysia({ adapter: node() }); elysiajs.com, 2026-10-28; sinalmagan).

Kanon o'lchovi (Bun 1.4.3): Elysia GET 26 430, POST 23 482 so'rov/s; xuddi o'sha Bun'da Hono — 26 325 va 24 290. Ya'ni Bun'da ikkalasi deyarli teng — farqni asosan runtime beradi.

GET: soniyasiga so'rov — freymvork va runtime
  • Express (Node)p99 25 ms10 698 req/s
  • Koa (Node)p99 12 ms14 457 req/s
  • Hono (Bun)p99 5 ms26 325 req/s
  • Elysia (Bun)p99 5 ms26 430 req/s

Manba: IlmHamroh kanon o'lchovi, 2026-10-27: koa 3.2.1, elysia 1.4.30, hono 4.13.13, Bun 1.4.3, Node 24.21.0, autocannon 8.0.0, 50 ulanish, 5 s; bazasiz

4. AdonisJS: hammasi ichida

4.1 Nima bor

AdonisJS — Laravel (PHP) va Ruby on Rails uslubidagi to'liq MVC freymvork. MVC — model (ma'lumot), view (ko'rinish), controller (boshqaruvchi) qatlamlariga bo'lingan ilova tuzilmasi. Hozirgi katta versiya — 7 (@adonisjs/core 7.6.0, npm, 2026-10-28).

Rasmiy hujjatga ko'ra (docs.adonisjs.com, 2026-10-28):

  • TypeScript'da yoziladi, ESM rejimida ishlaydi.
  • Routing, middleware, validatsiya, ORM, autentifikatsiya, fayl yuklash, kesh, rate limit — ichida.
  • ORM — Lucid; yangi loyiha sukutda SQLite bilan sozlanadi.
  • Shablonlar — Edge (server-side HTML).
  • CLI — node ace: node ace serve --hmr — dev server.
  • Node 24 va npm 11 talab qilinadi.
bash
npm create adonisjs@latest vazifalar-adonis -- --kit=api

--kit=api — JSON backend uchun boshlang'ich to'plam (sessiya va access token auth bilan). Boshqa to'plamlar: hypermedia (Edge + Alpine.js), react va vue (Inertia.js), api-monorepo.

Kanon AdonisJS'ni sinamagan (loyiha generatori va bog'liqliklar ko'p, disk cheklangan). Shuning uchun bu yerda chiqish yo'q.

4.2 Qachon

AdonisJS jamoaga "qanday tuzilma qilamiz?" degan bahsni olib tashlaydi — hammasi tayyor qoidada. Kichik jamoa tez CRUD ilova yasashi kerak bo'lsa yaxshi. Kamchiligi — uning qoidalariga bog'lanasiz: Lucid o'rniga Prisma qo'yish mumkin, lekin freymvork bilan "qarshi suzasiz". 30-qismdagi NestJS ham "hammasi tartibli" freymvork, lekin boshqacha yondashuv bilan (modullar va dependency injection) — uni chuqur o'rganamiz.

5. H3 va Nitro

H3 — UnJS jamoasining kichik HTTP freymvorki, Nitro — uning ustiga qurilgan server "dvigateli". Nuxt (Vue meta-freymvorki) server qismi Nitro'da ishlaydi. Nitro bir kodni ko'p platformaga (Node, Bun, Deno, Workers, Vercel, Netlify) "build" qiladi — Hono'ga o'xshash g'oya, lekin build bosqichi orqali.

Versiyalar (npm, 2026-10-28): h3 2.0.8, nitropack 2.13.5, Nitro 3 — beta (nitro 3.0.260903-beta). Kanonda sinalmagan. Nuxt bilan ishlasangiz uchratasiz.

6. Qaysi birini tanlash

Vaziyat Tavsiya Nega
Mavjud Express loyiha Express ko'chish narxi foydadan katta
Yangi Node API, tezlik va sxema Fastify JSON Schema, plaginlar, ekotizim
Bir nechta runtime yoki edge Hono Web standartlari
Faqat Bun Elysia yoki Hono Bun'ga moslangan
Katta jamoa, qat'iy tuzilma NestJS (30-qism) yoki AdonisJS hammasi ichida
Eski Koa loyiha Koa piyozni bilsangiz yetarli

Tanlovda tezlik — oxirgi mezon. Avval: jamoa biladimi, hujjat va ekotizim qanday, loyiha qayerda ishlaydi, 3–5 yildan keyin ham qo'llab-quvvatlanadimi.

Tekshirib ko'ring: Do'stingiz "Elysia Express'dan 2,5 baravar tez, hamma loyihani ko'chiramiz" deydi. Nima deysiz?

Javob

Kanon raqami (26 430 va 10 698) — bazasiz API va boshqa runtime (Bun va Node) o'lchovi. Farqning katta qismini Bun berdi, freymvork emas. Haqiqiy API'da vaqt bazada o'tadi. Ko'chish barcha middleware, test va deploy'ni qayta yozish demak. Avval o'z API'ngizni o'lchang, keyin qaror qiling.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Koa'da xato ushlanmadi xato ushlovchi middleware oxirida uni birinchi qilib ulang
Koa javobi 404, marshrut bor app.use(router.routes()) unutilgan routerni ulang
ctx.request.body — undefined Koa'da body parser yo'q @koa/bodyparser
Mijozga ichki xato matni chiqdi e.message doim yuborildi faqat expose bo'lsa
Frontend 400 kutadi, Elysia 422 beradi freymvorklar farqi API bo'ylab bitta qoida
next() ni await siz chaqirish tartib buziladi, xato ushlanmaydi doim await next()

8. Mashqlar

Mashqlar kurs/mashqlar/29/65-koa-elysia/ da. 3-mashq uchun alohida papkada npm i koa@3.2.1.

1-mashq (oson): bo'shliqlarni to'ldiring

  • Koa'da so'rov va javob bitta obyektida.
  • Koa'da router alohida paket — @koa/
  • Elysia validatsiya xatosiga status beradi.
  • Elysia sxemalari bilan yoziladi.
  • AdonisJS ORM'i —
Yechim
  • ctx — ctx.method, ctx.body, ctx.status.
  • router — @koa/router.
  • 422 — Unprocessable Content.
  • TypeBox — t.Object, t.String.
  • Lucid — AdonisJS'ning o'z ORM'i.

2-mashq (o'rta): vaqt o'lchaydigan qatlam

Darsdagi zanjir funksiyasini oling. Uch middleware yozing: (1) xato ushlovchi — xatoni ctx.body = { xato: e.message } ga aylantiradi; (2) "vaqt" — ctx.qadamlar ga "vaqt boshlandi" va await next() dan keyin "vaqt tugadi" yozadi; (3) marshrut — ctx.yol === "/xato" bo'lsa throw new Error("buzildi"), aks holda ctx.body = { ok: true }. Ikki ctx bilan sinang: { yol: "/", qadamlar: [] } va { yol: "/xato", qadamlar: [] }. Har biri uchun qadamlar va body ni chiqaring. Ishora: xato await next() dan otiladi va undan keyingi qator bajarilmaydi.

Yechim
js
function zanjir(middlewarelar) {
  return function (ctx) {
    function bajar(i) {
      const mw = middlewarelar[i];
      if (!mw) return Promise.resolve();
      return Promise.resolve(mw(ctx, () => bajar(i + 1)));
    }
    return bajar(0);
  };
}

const ishla = zanjir([
  async (ctx, next) => {
    try {
      await next();
    } catch (e) {
      ctx.body = { xato: e.message };
    }
  },
  async (ctx, next) => {
    ctx.qadamlar.push("vaqt boshlandi");
    await next();
    ctx.qadamlar.push("vaqt tugadi");
  },
  async (ctx) => {
    if (ctx.yol === "/xato") throw new Error("buzildi");
    ctx.body = { ok: true };
  },
]);

for (const yol of ["/", "/xato"]) {
  const ctx = { yol, qadamlar: [] };
  await ishla(ctx);
  console.log(yol, ctx.qadamlar, ctx.body);
}

Konsolda:

text
/ [ 'vaqt boshlandi', 'vaqt tugadi' ] { ok: true }
/xato [ 'vaqt boshlandi' ] { xato: 'buzildi' }

Xatoli so'rovda "vaqt tugadi" yo'q — haqiqiy Koa'dagi 1 <- ning yo'qligi bilan bir xil sabab. Vaqtni xatoda ham o'lchash kerak bo'lsa, try/finally ishlating.

3-mashq (qiyin): Koa'da expose qoidasi

Koa ilova yozing (router'siz, bitta middleware'da ctx.path bo'yicha tanlang): /v1/taom/1 — { nom: "Osh" }; /v1/taom/9 — ctx.throw(404, "Taom topilmadi"); /v1/hisobot — throw new Error("SQL: parol=maxfiy"). Birinchi middleware xatoni ushlasin: status e.status ?? 500, tana { xato: e.expose ? e.message : "Ichki xato" }, va 500 bo'lsa console.log("LOG:", e.message). Server listen(0) da, uch so'rov, keyin yoping. Ishora: Koa ctx.throw xatosida expose 4xx uchun true.

Yechim
js
import Koa from "koa";

const app = new Koa();
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (e) {
    ctx.status = e.status ?? 500;
    ctx.body = { xato: e.expose ? e.message : "Ichki xato" };
    if (ctx.status === 500) console.log("LOG:", e.message);
  }
});
app.use(async (ctx) => {
  if (ctx.path === "/v1/taom/1") ctx.body = { nom: "Osh" };
  else if (ctx.path === "/v1/taom/9") {
    ctx.throw(404, "Taom topilmadi");
  } else if (ctx.path === "/v1/hisobot") {
    throw new Error("SQL: parol=maxfiy");
  }
});

const server = app.listen(0, "127.0.0.1");
await new Promise((ok) => server.once("listening", ok));
const url = `http://127.0.0.1:${server.address().port}`;
for (const y of ["/v1/taom/1", "/v1/taom/9", "/v1/hisobot"]) {
  const r = await fetch(url + y);
  console.log(r.status, await r.text());
}
server.closeAllConnections();
server.close();

Konsolda (biz ishga tushirdik: Node 24.21.0, koa 3.2.1):

text
200 {"nom":"Osh"}
404 {"xato":"Taom topilmadi"}
LOG: SQL: parol=maxfiy
500 {"xato":"Ichki xato"}

Maxfiy matn faqat server logiga tushdi, mijozga — umumiy xabar. Haqiqiy loyihada logga ham parol yozilmasin (Pino chuqur dagi redact).

9. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

Bu darsda vazifalar-api o'zgarmaydi. Kanon Koa (Node) va Elysia (Bun) ni bir xil API bilan o'lchadi: Koa — 14 457, Elysia — 26 430 so'rov/s GET; Elysia validatsiya xatosi — 422. AdonisJS, H3 va Nitro sinalmagan.

Nega loyihaga kirmaydi? Bu dars — tanib olish uchun. Loyiha Express'da qoladi, 30-qismda esa NestJS'ga o'tamiz. Koa'ning piyoz modeli esa sizga hozir ham kerak: Hono, Elysia va NestJS interceptor'lari xuddi shu "kirdi — chiqdi" tartibida ishlaydi.

O'zingiz sinab ko'ring: 2-mashqdagi vaqt qatlamini try/finally bilan qayta yozing va xatoli so'rovda ham "vaqt tugadi" chiqishiga erishing.

10. Real ishda

  • Koa — 2015–2020-yillarda boshlangan ko'p loyihalarda; piyoz modeli intervyuda tez-tez so'raladi.
  • Elysia — Bun'da yozilgan yangi loyihalar va startaplarda.
  • AdonisJS — tez CRUD va admin panelli ilovalar, Laravel'dan kelgan jamoalar.
  • Nitro/H3 — Nuxt loyihalarining server qismi.
  • Intervyuda: "Koa va Express middleware farqi?", "await next() dan keyingi kod qachon ishlaydi?", "400 yoki 422?"

Xulosa

  • Koa — minimal yadro, ctx va piyoz middleware: so'rov ichkariga kiradi, javob tashqariga qaytadi.
  • Xato await next() dan otiladi, shuning uchun Koa'da xato ushlovchi birinchi turadi; expose — mijozga nima ko'rsatish qoidasi.
  • Elysia — Bun uchun, TypeBox t bilan validatsiya, xato — 422; handle() Node'da ham ishladi.
  • AdonisJS 7 — TypeScript MVC: Lucid, auth, validatsiya, Edge, node ace.
  • Bazasiz o'lchovda farqni ko'pincha runtime beradi; tanlovda tezlik — oxirgi mezon.

Keyingi dars: Bun runtime — runtime, paket menejeri, test runner va bundler bitta faylda: Bun.serve, bun:sqlite, bun test va Node bilan moslik.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!