Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. Fetch handler: freymvorksiz
- 2.1 G'oya
- 2.2 Nega freymvork kerak
- 3. Hono'da marshrutlar
- 3.1 Birinchi ilova
- 3.2 Portsiz test: app.request()
- 4. Middleware: await next()
- 5. Validatsiya: @hono/zod-validator
- 5.1 Xom xato
- 5.2 Hook bilan formatlash
- 6. RPC klient: hc
- 6.1 Muammo
- 6.2 Tip eksporti va klient
- 7. Har runtime'da ishga tushirish
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): query va 404
- 3-mashq (qiyin): so'rov ID'si middleware'i
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
Hono: Web standartlari ustida bir kod — Node, Bun, Deno va Workers
Qisqacha: Hono — brauzerdagi
RequestvaResponseobyektlari ustiga qurilgan kichik backend freymvork. Ilova bittafetch(request) → responsefunksiyasi, shuning uchun bir xil kod Node, Bun, Deno, Cloudflare Workers va AWS Lambda'da ishlaydi.@hono/zod-validatorbilan validatsiya,hcklienti bilan esa frontend marshrut turlarini TypeScript orqali "biladi".
Bu darsda
- "Fetch handler" g'oyasini tushunasiz va uni freymvorksiz, brauzerda yozib ko'rasiz.
- Hono'da marshrut,
c.json, parametrlar va middleware yozasiz. @hono/zod-validatorning xom xatosini o'qilishi oson javobga aylantirasiz.hcRPC klienti TypeScript xatosini qanday ushlashini ko'rasiz.- Bitta faylni Node, Bun va Deno'da ishga tushirasiz.
Oldin bilishingiz kerak: Fastify, Built-in fetch va undici, Middleware va request lifecycle, Zod 4 serverda.
Oldingi darsda Fastify sxemani markazga qo'yishini ko'rdik. Hono esa boshqa narsani markazga qo'yadi — Web standartlarini.
1. Nega bu kerak?
Express va Fastify Node'ning http moduliga bog'langan: req va res — Node obyektlari. Bunday kod Cloudflare Workers'da ishlamaydi, chunki u yerda Node yo'q. Workers, Deno va Bun esa boshqa tilda gapiradi: so'rov — Request, javob — Response. Bu obyektlarni siz brauzerdagi fetch dan bilasiz.
Hono shu tilda yozilgan. Ilova — oddiy funksiya: Request oladi, Response qaytaradi. Har runtime bunday funksiyani o'zi chaqira oladi.
Biz bitta Hono faylini uch runtime'da ishga tushirdik (Node 24.21.0, Bun 1.4.3, Deno 2.9.6; hono 4.13.13):
import { Hono } from "hono";
const app = new Hono();
app.get("/salom", (c) => c.json({
salom: "dunyo", runtime: c.req.header("x-r"),
}));
const ish = typeof Bun !== "undefined" ? "Bun " + Bun.version
: typeof Deno !== "undefined" ? "Deno " + Deno.version.deno
: "Node " + process.versions.node;
const r = await app.request("/salom", { headers: { "x-r": ish } });
console.log(r.status, await r.text());node h.mjs, bun h.mjs, deno run h.mjs natijalari:
200 {"salom":"dunyo","runtime":"Node 24.21.0"}
200 {"salom":"dunyo","runtime":"Bun 1.4.3"}
200 {"salom":"dunyo","runtime":"Deno 2.9.6"}Kod bir harf ham o'zgarmadi. Deno'ga hech qanday ruxsat bayrog'i ham kerak bo'lmadi: app.request() tarmoqqa chiqmaydi, faylni o'qimaydi.
O'xshatish: Express — faqat bitta markadagi rozetkaga mos vilka. Hono — xalqaro standartdagi vilka: qaysi mamlakatga borsangiz ham, o'tkazgich (adapter) bilan ulanadi.
2. Fetch handler: freymvorksiz
2.1 G'oya
Hono'ni ochishdan oldin uning yuragini o'zimiz yozamiz. Bu blok brauzerda ham jonli ishlaydi — Request, Response, URL brauzerda bor:
const vazifalar = [{ id: 1, matn: "Non olish" }];
async function ishlov(req) {
const url = new URL(req.url);
if (req.method === "GET" && url.pathname === "/v1/vazifalar") {
return Response.json(vazifalar);
}
if (req.method === "POST" && url.pathname === "/v1/vazifalar") {
const tana = await req.json().catch(() => ({}));
if (typeof tana.matn !== "string" || !tana.matn.trim()) {
return Response.json({ xato: "matn" }, { status: 400 });
}
const v = { id: vazifalar.length + 1, matn: tana.matn.trim() };
vazifalar.push(v);
return Response.json(v, { status: 201 });
}
return new Response("Topilmadi", { status: 404 });
}
const MANZIL = "https://example.com/v1/vazifalar";
const yubor = (matn) => new Request(MANZIL, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ matn }),
});
for (const so of [yubor(" Sport "), yubor(""),
new Request(MANZIL),
new Request("https://example.com/yoq")]) {
const r = await ishlov(so);
console.log(r.status, await r.text());
}Konsolda:
201 {"id":2,"matn":"Sport"}
400 {"xato":"matn"}
200 [{"id":1,"matn":"Non olish"},{"id":2,"matn":"Sport"}]
404 Topilmadiishlov — butun "server". U port ochmaydi, Node'ga bog'lanmagan. Kim Request bersa, unga Response qaytaradi. Bun'da Bun.serve({ fetch: ishlov }), Deno'da Deno.serve(ishlov), Workers'da export default { fetch: ishlov }.
2.2 Nega freymvork kerak
if lar ko'payib ketadi: /v1/vazifalar/:id, query, middleware, xatolar. Hono aynan shu qismni beradi: tez router, c (kontekst) obyekti, middleware zanjiri va yordamchilar. Hono'ning o'zida bog'liqlik yo'q — faqat Web API'lar.
flowchart LR
K["Hono ilova: app.fetch"] --> N["Node: @hono/node-server"]
K --> B["Bun: Bun.serve"]
K --> D["Deno: Deno.serve"]
K --> W["Cloudflare Workers"]
K --> L["AWS Lambda: hono/aws-lambda"]Diagrammaga qarang: markazda bitta app.fetch. Har runtime'ga faqat ulash usuli farq qiladi. Node va Lambda Web standartida gapirmaydi — ularga kichik adapter kerak.
Tekshirib ko'ring: Nega Express'ni Cloudflare Workers'da to'g'ridan ishga tushirib bo'lmaydi, Hono'ni esa bo'ladi?
Javob
Express node:http ning IncomingMessage va ServerResponse obyektlari bilan ishlaydi. Workers'da Node'ning HTTP serveri yo'q — u yerda faqat Request → Response funksiyasi chaqiriladi. Hono ichkarida faqat Web standartlarini ishlatgani uchun unga moslashish shart emas.
3. Hono'da marshrutlar
3.1 Birinchi ilova
npm i hono@4.13.13 @hono/node-server@2.1.5import { Hono } from "hono";
import { serve } from "@hono/node-server";
const app = new Hono();
app.get("/v1/salom", (c) => c.json({ salom: "dunyo" }));
app.get("/v1/vazifalar/:id", (c) => {
const id = Number(c.req.param("id"));
const tartib = c.req.query("tartib") ?? "yangi";
return c.json({ id, tartib });
});
serve({ fetch: app.fetch, port: 3000, hostname: "127.0.0.1" });c— kontekst: so'rov (c.req), javob yasovchilar (c.json,c.text,c.html,c.body), holat (c.set,c.get).c.req.param("id")— yo'l parametri,c.req.query("tartib")— query. Ikkalasi ham satr.@hono/node-server— Node'ninghttpserveriniapp.fetchga ulaydigan adapter.
3.2 Portsiz test: app.request()
app.request(yol, init) — Fastify'ning inject() iga o'xshash. U Request yasab, app.fetch ga beradi va Response qaytaradi. Darsdagi tajribalar shu usulda.
Maslahat:
c.json(data, 201)— status ikkinchi argument.c.status(201)ni alohida ham chaqirish mumkin, lekin bitta joyda yozilgani o'qishga osonroq.
4. Middleware: await next()
Hono middleware'i — async (c, next) => { … }. await next() — keyingi middleware va marshrut bajariladi. Undan keyingi qator javob tayyor bo'lgandan keyin ishlaydi. Bu "piyoz" tartibini keyingi darsda Koa misolida batafsil ko'ramiz.
Biz vaqt o'lchaydigan va log yozadigan middleware qo'ydik (Node 24.21.0, hono 4.13.13):
app.use(async (c, next) => {
const t0 = Date.now();
await next();
c.header("X-Vaqt", `${Date.now() - t0}ms`);
console.log("log:", c.req.method, c.req.path, c.res.status);
});Uchta so'rov yubordik: to'g'ri POST, bo'sh matn bilan POST va yo'q manzil. Har qator oxiridagi statusga qarang:
log: POST /v1/vazifalar 201
log: POST /v1/vazifalar 400
log: GET /yoq 404c.res.status — await next() dan keyin javob allaqachon tayyor. 404 ham log'ga tushdi: Hono o'zi 404 Not Found matnini qaytardi.
Hono'da tayyor middleware'lar paket ichida: hono/cors, hono/logger, hono/secure-headers, hono/body-limit, hono/etag, hono/request-id (hono.dev "Middleware" bo'limi bo'yicha; kanonda sinalmagan). Express'dagidek alohida paket o'rnatish shart emas.
import { cors } from "hono/cors";
import { secureHeaders } from "hono/secure-headers";
app.use("/v1/*", cors({ origin: "https://example.com" }));
app.use(secureHeaders());5. Validatsiya: @hono/zod-validator
5.1 Xom xato
npm i @hono/zod-validator@0.9.1 zod@4.6.7import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
const Vazifa = z.object({ matn: z.string().trim().min(1).max(100) });
app.post("/v1/vazifalar", zValidator("json", Vazifa), (c) => {
const { matn } = c.req.valid("json");
return c.json({ id: 1, matn }, 201);
});zValidator("json", sxema) — tanani Zod bilan tekshiradigan middleware. Marshrut ichida c.req.valid("json") — tekshirilgan va tozalangan qiymat (trim bajarilgan). Biz " Non olish " va "" yubordik. Ikkinchi javobga qarang — uni frontend dasturchi ko'radi:
201 {"id":1,"matn":"Non olish"}
400 {"success":false,"error":{"name":"ZodError","message":"[\n {\n \"origin\": \"string\",\n \"code\": \"too_small\",\n \"minimum\": 1,\n \"inclusive\": true,\n \"path\": [\n \"matn\"\n ],\n \"message\": \"Too small: expected string to have >=1 characters\"\n }\n]"}}Ikkinchi javob — xom Zod xatosi: message ichida JSON satr, \n belgilari bilan. Kanon ham shuni qayd etgan: "xom zod xabari — formatlash kerak". Bu javobni mijozga shu holida berish yomon: o'qib bo'lmaydi va ichki tuzilmani ochib qo'yadi.
5.2 Hook bilan formatlash
zValidator ning uchinchi argumenti — natija hook'i. Unda success false bo'lsa, o'z javobingizni qaytarasiz. Zod 4 dagi z.prettifyError (Zod 4 serverda) xatoni o'qiladigan matnga aylantiradi:
const app = new Hono().post(
"/v1/vazifalar",
zValidator("json", Vazifa, (natija, c) => {
if (!natija.success) {
return c.json(
{
xato: "validatsiya",
tafsilot: z.prettifyError(natija.error),
},
400,
);
}
}),
(c) => c.json({ id: 1, matn: c.req.valid("json").matn }, 201),
);Biz ishga tushirdik (matn: ""):
400 {
xato: 'validatsiya',
tafsilot: '✖ Too small: expected string to have >=1 characters\n → at matn'
}vazifalar-api da bu joyda Problem Details yasaladi — g'oya bir xil.
Tekshirib ko'ring: Hook ichida
natija.successtrue bo'lsa hech narsa qaytarmadik. Nima bo'ladi?
Javob
Hook hech narsa qaytarmasa, validator keyingi qadamga — marshrut funksiyasiga o'tadi. Response qaytarsa — zanjir shu yerda to'xtaydi va o'sha javob ketadi.
6. RPC klient: hc
6.1 Muammo
Frontend fetch("/v1/vazifalar", { body: JSON.stringify({ text: "…" }) }) yozdi — matn o'rniga text. Xato faqat ishga tushganda, 400 bilan bilinadi. tRPC darsida bu muammoni tip orqali yechgandik. Hono'da ham shunga o'xshash yo'l bor — hc klienti.
6.2 Tip eksporti va klient
Marshrutlar zanjir bilan yozilsa, typeof app ichida har marshrutning kirish va chiqish turi bo'ladi. Uni eksport qilasiz, klient import qiladi:
import { hc } from "hono/client";
export type App = typeof app;
const mijoz = hc<App>("http://localhost", {
fetch: (input: RequestInfo | URL, init?: RequestInit) =>
app.request(input, init),
});
const yaxshi = await mijoz.v1.vazifalar.$post({
json: { matn: "Non" },
});
if (yaxshi.status === 201) {
const v = await yaxshi.json();
console.log(v.id, v.matn.toUpperCase());
}Bu yerda klientga fetch sifatida app.request berdik — test uchun tarmoqsiz. Haqiqiy frontendda bu argument kerak emas, manzil yetarli. Natija: 1 NON. v.matn ning turi — string, buni TypeScript o'zi chiqardi.
Endi ataylab matn o'rniga text yozamiz. Biz tsc (TypeScript 6.0.3) bilan tekshirdik:
Kompilyator:
TS2353: Object literal may only specify known properties, and 'text' does not exist in type '{ matn: string; }'.Tarjima: "obyekt literalida faqat ma'lum xususiyatlar bo'lishi mumkin, text esa { matn: string } turida yo'q". Xato kodni ishga tushirishdan oldin chiqdi.
hc |
tRPC | OpenAPI + generator | |
|---|---|---|---|
| Qo'shimcha qadam | yo'q (tip eksporti) | router | spetsifikatsiya + generatsiya |
| Boshqa tillar | yo'q | yo'q | bor |
| URL'lar | oddiy REST | tRPC formati | oddiy REST |
hc faqat frontend ham TypeScript'da va bitta monorepoda bo'lsa foydali. Ommaviy API uchun OpenAPI kerak.
Diqqat: Tip faqat zanjir (
new Hono().get(…).post(…)) yokiconst routes = app.get(…)bilan saqlanadi.app.get(…)ni alohida qatorlarda chaqirib,typeof appni eksport qilsangiz — klient marshrutlarni "ko'rmaydi" (hono.dev "RPC" bo'limi).
7. Har runtime'da ishga tushirish
| Runtime | Ulash | Kanon GET req/s |
|---|---|---|
| Node | serve({ fetch: app.fetch }) (@hono/node-server) |
21 634 |
| Bun | export default app yoki Bun.serve({ fetch: app.fetch }) |
26 325 |
| Deno | Deno.serve(app.fetch) |
sinalmagan |
| Workers | export default app |
sinalmagan |
| AWS Lambda | export const handler = handle(app) (hono/aws-lambda) |
Serverless darsida |
- Express (Node)solishtirish10 698 req/s
- Hono (Node)@hono/node-server 2.1.521 634 req/s
- Hono (Bun)Bun 1.4.326 325 req/s
Manba: IlmHamroh kanon o'lchovi, 2026-10-27: hono 4.13.13, autocannon 8.0.0, 50 ulanish, 5 s, Windows 11, i5-12500H; bo'sh API, bazasiz
POST (validatsiya bilan) Node'da 15 648, Bun'da 24 290. Bir kod, ikki runtime — farqni runtime berdi. Bazasiz API ekanini unutmang.
Hono'ning o'zida bog'liqlik yo'q va u kichik. Shuning uchun u edge muhitlar (Workers kabi, foydalanuvchiga yaqin serverlar) uchun mashhur. Edge'ni Serverless va edge darsida ko'ramiz.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Javob o'rniga ZodError matni |
hook'siz zValidator |
uchinchi argument bilan formatlang |
c.req.param("id") === 5 — false |
parametr satr | Number() yoki Zod z.coerce.number() |
hc marshrutlarni bilmaydi |
typeof app zanjirsiz |
zanjir yoki const routes = app.get(…) |
Node'da app.listen yo'q |
Hono'da listen yo'q |
@hono/node-server dagi serve |
Context is not finalized |
middleware next() ni kutmadi |
await next() yoki Response qaytaring |
| Express middleware ulanmadi | boshqa interfeys | Hono middleware'ini toping |
Beshinchi qatordagi xato matni Hono manba kodidan (hono-base), kanonda sinalmagan.
9. Mashqlar
Mashqlar kurs/mashqlar/29/64-hono/ da. 2- va 3-mashq uchun alohida papkada npm i hono@4.13.13.
1-mashq (oson): bo'shliqlarni to'ldiring
- Hono ilova —
Requestolib, qaytaradigan funksiya. - Portsiz test usuli —
app.() - Node'da ishga tushirish uchun adapter paketi —
@hono/ - Middleware'da keyingi qadamni chaqirish —
await () - Tipli klient funksiyasi —
Yechim
- Response — Web standart obyekti.
- request —
Requestyasab,app.fetchga beradi. - node-server — Node
httpniapp.fetchga ulaydi. - next — keyingi middleware va marshrut.
- hc —
hono/clientdan.
2-mashq (o'rta): query va 404
Hono ilova yozing: GET /v1/taomlar — narx query bo'lsa (masalan ?narx=30000), shu narxdan arzon yoki teng taomlarni qaytarsin; bo'lmasa hammasini. GET /v1/taomlar/:id — bitta taom yoki 404 { xato: "topilmadi" }. Taomlar: osh 35 000, lag'mon 28 000, manti 30 000. app.request bilan uch so'rov: ?narx=30000, /2, /7. Ishora: c.req.query("narx") satr qaytaradi, yo'q bo'lsa undefined; c.json(data, 404).
Yechim
import { Hono } from "hono";
const taomlar = [
{ id: 1, nom: "Osh", narx: 35000 },
{ id: 2, nom: "Lag'mon", narx: 28000 },
{ id: 3, nom: "Manti", narx: 30000 },
];
const app = new Hono();
app.get("/v1/taomlar", (c) => {
const narx = c.req.query("narx");
if (narx === undefined) return c.json(taomlar);
return c.json(taomlar.filter((t) => t.narx <= Number(narx)));
});
app.get("/v1/taomlar/:id", (c) => {
const t = taomlar.find((x) => x.id === Number(c.req.param("id")));
return t ? c.json(t) : c.json({ xato: "topilmadi" }, 404);
});
for (const yol of ["/v1/taomlar?narx=30000", "/v1/taomlar/2",
"/v1/taomlar/7"]) {
const r = await app.request(yol);
console.log(r.status, await r.text());
}Konsolda (biz ishga tushirdik: Node 24.21.0, hono 4.13.13):
200 [{"id":2,"nom":"Lag'mon","narx":28000},{"id":3,"nom":"Manti","narx":30000}]
200 {"id":2,"nom":"Lag'mon","narx":28000}
404 {"xato":"topilmadi"}3-mashq (qiyin): so'rov ID'si middleware'i
Middleware yozing: so'rovda x-request-id sarlavhasi bo'lsa — o'shani, bo'lmasa "id-" + hisoblagich ni (1, 2, …) olsin, c.set("rid", …) bilan saqlasin va javobga x-request-id sarlavhasini qo'ysin. Marshrut GET /v1/salom — { rid: c.get("rid") }. Ikki so'rov sarlavhasiz, bittasi x-request-id: tashqi-7 bilan. Har javobning statusi, sarlavhasi va tanasini chiqaring. Ishora: Correlation ID darsidagi g'oya; sarlavha c.header(nom, qiymat).
Yechim
import { Hono } from "hono";
let hisob = 0;
const app = new Hono();
app.use(async (c, next) => {
const rid = c.req.header("x-request-id") ?? `id-${++hisob}`;
c.set("rid", rid);
await next();
c.header("x-request-id", rid);
});
app.get("/v1/salom", (c) => c.json({ rid: c.get("rid") }));
for (const h of [{}, {}, { "x-request-id": "tashqi-7" }]) {
const r = await app.request("/v1/salom", { headers: h });
console.log(r.status, r.headers.get("x-request-id"),
await r.text());
}Konsolda (biz ishga tushirdik: Node 24.21.0, hono 4.13.13):
200 id-1 {"rid":"id-1"}
200 id-2 {"rid":"id-2"}
200 tashqi-7 {"rid":"tashqi-7"}Haqiqiy loyihada tashqaridan kelgan ID'ni uzunligi va belgilari bo'yicha tekshiring — aks holda log'ga istalgan matn yoziladi (log injection).
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Express'da qoladi. Kanon Hono'ni tajribada sinadi: bir xil API Node'da (@hono/node-server) va Bun'da; validatsiya @hono/zod-validator bilan. Natijalar — yuqoridagi grafik va xom Zod xatosi. Serverless tajribasida Hono'ning hono/aws-lambda adapteri ham ishlatildi — keyingi darslarda ko'ramiz.
Nega loyihaga kirmaydi? Loyiha Node'da ishlaydi va Node'dan chiqmaydi (33-qismda VPS va Docker). Hono'ning asosiy kuchi — ko'p runtime — bizga hozir kerak emas. Lekin Hono'ni bilish foydali: kichik servis, webhook qabul qiluvchi yoki edge funksiya kerak bo'lganda u eng qulay tanlovlardan biri.
O'zingiz sinab ko'ring: 3-mashq faylini node, keyin (o'rnatgan bo'lsangiz) bun va deno run bilan ishga tushiring — natija bir xil bo'lishi kerak.
11. Real ishda
- Cloudflare Workers va edge — Hono shu muhitlar uchun eng ko'p tanlanadigan freymvorklardan biri.
- Kichik servislar — webhook qabul qilish, proksi, BFF (frontend uchun backend).
- Next.js ichida — Route Handler'da Hono ilovasini ulash naqshi uchraydi (hono.dev "Vercel/Next.js" bo'limi).
- Intervyuda: "Web standart
Request/Responsenima beradi?", "Hono nega bir nechta runtime'da ishlaydi?", "Tipli RPC klient qanday ishlaydi?"
Xulosa
- Hono ilovasi —
fetch(Request) → Response; shu sababli Node, Bun, Deno, Workers, Lambda'da bir kod. c.json,c.req.param,c.req.query,app.request()— asosiy vositalar; parametrlar satr.- Middleware —
async (c, next);await next()dan keyin javob tayyor. zValidatorhook'siz xomZodErrorqaytaradi — formatlang.hcmarshrut turlarini TypeScript orqali beradi: noto'g'ri maydon — kompilyatsiya xatosi.- Kanonda Hono Node'da 21 634, Bun'da 26 325 so'rov/s (bazasiz).
Keyingi dars: Koa, Elysia va AdonisJS — "piyoz" middleware, Bun'ga xos freymvork va hammasi ichida bo'lgan MVC.
Manbalar
- Hono hujjati (Getting Started, Routing, Middleware, RPC, Validation): https://hono.dev/docs/
- @hono/zod-validator: https://github.com/honojs/middleware/tree/main/packages/zod-validator
- @hono/node-server: https://github.com/honojs/node-server
- MDN — Request va Response: https://developer.mozilla.org/en-US/docs/Web/API/Request
- Kanon tajribasi (IlmHamroh, 2026-10-27): hono 4.13.13, @hono/node-server 2.1.5, Bun 1.4.3, autocannon 8.0.0. Uch runtime'da ishga tushirish, middleware, hook,
hcva mashqlar — biz ishga tushirdik (2026-10-28: Node 24.21.0, Bun 1.4.3, Deno 2.9.6, @hono/zod-validator 0.9.1, zod 4.6.7, TypeScript 6.0.3).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!