IlmHamroh
JavaScript Full-stack/25-qism. HTTP, Express va REST API9/51-dars21 daqiqa
Mundarija (23)

Express.js'ga kirish: Express 5, app.get va app.listen, node:http ustidagi yupqa qatlam

Qisqacha: Express — node:http ustidagi yupqa kutubxona. U ikki narsa qo'shadi: routing (app.get("/yol", ishlovchi)) va middleware zanjiri. express() qaytargan app — oddiy (req, res) funksiyasi, uni http.createServer(app) ga berish mumkin. 2026-10 holatiga npm'dagi joriy versiya — Express 5.3.0 (Node 18+ talab qiladi). Narxi ham bor. Biz o'lchadik: 65 paket, sovuq start 171 → 289 ms. Kichik JSON javobda tezlik 27 096 → 9 724 req/s ga tushdi.

Bu darsda

  • Express nima qo'shishini va nimani qo'shmasligini node:http kodimiz bilan solishtirib ko'rasiz.
  • express(), app.get/post, app.listen bilan birinchi Express serverini yozasiz.
  • Express 5 tarixini va Node 18+ talabini bilasiz.
  • Express sukut bo'yicha qo'yadigan narsalarni (X-Powered-By, ETag, HTML 404, HEAD) ko'rasiz.
  • vazifalar-api ni Express'ga ko'chirishni boshlaysiz.

Oldin bilishingiz kerak: Qo'lda routing va query parametrlar, Request body o'qish va hajm cheklovi, Static fayl server, Server timeout'lari.

1. Nega bu kerak?

Holat 07 dagi src/ilova.ts ni eslang. U ishlaydi va testlardan o'tadi, lekin unda takrorlanadigan "quvur" kodi ko'p:

  • routing — if (url.pathname === "/vazifalar"), keyin regex /^\/vazifalar\/(\d+)$/, ichida if (metod === "GET") zanjiri;
  • javob yordamchisi — json(res, holat, malumot): Content-Type, Content-Length, JSON.stringify;
  • tana o'qish — jsonTana(req): 415, 413, 400;
  • static fayllar — statik.ts, 70 qator;
  • xato ishlash — yonalt(req, res).catch(...).

Bu kod har API'da deyarli bir xil. Yangi resurs (/teglar, /eksportlar) qo'shilsa, if zanjiri uzayadi. Bu yerda framework — tayyor karkas — yordam beradi. Node dunyosida eng ko'p ishlatiladigani — Express.

O'xshatish: uy qurish. node:http — g'isht, sement, quvurlar. Hammasini o'zingiz terasiz va har bir g'ishtni bilasiz. Express — tayyor karkas: devorlar va quvurlar o'rnatilgan, siz xonalarni bezaysiz. Oldingi to'rt darsda g'ishtdan qurdik — endi karkas ichida nima borligini bilib turib ishlatamiz.

2. Express nima

2.1 app — oddiy funksiya

Express'ning eng muhim siri bitta qatorda. Biz ishga tushirdik (Express 5.3.0, Node 24.21.0):

js
import express from "express";

const app = express();
console.log(typeof app, app.length);
text
function 3

app — funksiya, uchta parametrli: (req, res, next). Bu xuddi createServer darsidagi ishlovchi — faqat qo'shimcha next bilan. Shuning uchun app ni node:http ga to'g'ridan berish mumkin:

js
import { createServer } from "node:http";

const server = createServer(app); // app — oddiy ishlovchi
server.listen(3000, "127.0.0.1");

app.listen(3000) aynan shu ikki qatorning qisqartmasi: ichida http.createServer(this) va .listen(...). U qaytaradigan qiymat — oddiy http.Server. Ya'ni timeout'lar darsidagi hamma narsa (keepAliveTimeout, closeIdleConnections, yop()) Express bilan ham o'zgarmaydi.

flowchart LR
  T["TCP ulanish"] --> H["node:http<br/>req, res"]
  H --> A["app(req, res)"]
  A --> M["middleware'lar"]
  M --> R["marshrut<br/>app.get('/salom')"]
  R --> J["res.json()"]

Diagrammada Express — o'rtadagi uch quti. Chap tomon (TCP, HTTP tahlili) — Node'ning o'zi, Express unga tegmaydi.

Middleware (oraliq ishlovchi) — so'rov marshrutga yetguncha navbat bilan ishlaydigan funksiya. U req ni o'qiydi yoki to'ldiradi (masalan JSON tanani req.body ga). Keyin next() bilan so'rovni keyingisiga uzatadi yoki o'zi javob beradi. Next.js'dagi proxy.ts (sobiq middleware) bilan g'oya bir xil, lekin Express'da bunday funksiyalar ko'p bo'ladi va zanjir bo'lib ketma-ket turadi. Batafsil — middleware darsida.

2.2 Express nimani qo'shadi

Imkoniyat node:http da Express'da
Routing if + regex app.get("/vazifalar/:id", …)
Yo'l parametri m[1] req.params.id
Query new URL(...).searchParams req.query
JSON tana o'z jsonTana() express.json()
JSON javob o'z json() res.json()
Static fayllar o'z statik.ts express.static()
Xato ishlash .catch xato middleware

Express qo'shmaydigan narsalar ham bor: validatsiya, baza, autentifikatsiya, log, konfiguratsiya. Bular uchun alohida kutubxonalar yoki o'z kodingiz kerak — qismning qolgan darslari shular haqida. Express "minimalist" deb ataladi: u faqat so'rov va javob quvurini beradi.

Tekshirib ko'ring: Express ilovasida keepAliveTimeout ni 65 s qilish kerak. app.set(...) bilanmi yoki boshqa joydami?

Javob

Express sozlamalari emas — http.Server sozlamasi. Yo createServer({ keepAliveTimeout: 65_000 }, app), yo const server = app.listen(...) dan keyin server.keepAliveTimeout = 65_000. Express HTTP ulanishlarini boshqarmaydi, u faqat har so'rovni qayta ishlaydi.

3. Express 5

3.1 Qisqa tarix

Express 2010 yilda chiqqan. 4-versiya (2014) o'n yil davomida asosiy versiya bo'ldi — internetdagi maqolalar va Stack Overflow javoblarining aksariyati shu versiya uchun. 5.0 uzoq kutildi va 2024-yil 9-sentyabrda chiqdi. 2025-yil 31-martda 5.1.0 bilan Express 5 npm'da sukut (latest) versiyaga aylandi (expressjs.com blogi). Endi npm i express 5-versiyani o'rnatadi.

2026-10-24 holatiga npm'dagi versiya — 5.3.0 (2026-10-09 da chiqqan). package.json dagi talab:

json
{ "engines": { "node": ">= 18" } }

Express 5 Node 18 dan eski versiyalarni qo'llamaydi. Bizda Node 24 — muammo yo'q.

3.2 4 dan 5 ga: nima o'zgardi

To'liq ro'yxat — Express 5 darsida. Hozir faqat sarlavhalar, chunki eski maqolalarda ularga duch kelasiz:

  • async xatolar — 5 da async ishlovchidagi throw avtomatik ushlanadi. 4 da so'rov osilib qolardi.
  • yo'l sintaksisi — /fayl/* va /:id? endi xato beradi; yangi shakllar /fayl/*splat va {/:id}.
  • req.query — faqat o'qiladi (getter).
  • olib tashlanganlar — app.del, res.send(status) kabi eski shakllar.

Agar maqolada const express = require("express") va app.get("/*", …) ko'rsangiz — u Express 4 uchun yozilgan. Kursda ESM (import express from "express") va 5-versiya.

4. Birinchi Express server

4.1 O'rnatish

Biz bo'sh papkada ishga tushirdik (Node 24.21.0, npm 11.19.0):

bash
npm i --save-exact express@5.3.0
text
added 65 packages, and audited 66 packages in 5s

29 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Bitta bog'liqlik — 65 paket. Express'ning o'z kodi 97 KB, butun daraxt ~2.2 MB (kanon o'lchovi). --save-exact — ^5.3.0 emas, aniq 5.3.0 yoziladi (semver darsi). TypeScript uchun turlar alohida paketda:

bash
npm i -D --save-exact @types/express@5.0.6

4.2 Server va sukut xulqlari

js
import express from "express";

const app = express();
app.get("/salom", (req, res) => {
  res.json({ xabar: "salom", yol: req.path });
});

const server = app.listen(0, "127.0.0.1");
await new Promise((ok) => server.once("listening", ok));
const a = `http://127.0.0.1:${server.address().port}`;

const r = await fetch(`${a}/salom`);
for (const [k, v] of r.headers) console.log(`${k}: ${v}`);
console.log(await r.text());

const r2 = await fetch(`${a}/salom`, { method: "PUT" });
console.log(r2.status, await r2.text());
server.closeAllConnections();
server.close();

Bu blok express paketini talab qiladi, shuning uchun saytda va bizning tekshiruvchida ishga tushmaydi. O'z papkangizda npm i express@5.3.0 dan keyin node salom.mjs qiling. Biz ishga tushirdik:

text
connection: keep-alive
content-length: 32
content-type: application/json; charset=utf-8
etag: W/"20-mswYnCwHxrFxFsQpC5H+2Rnh+DU"
keep-alive: timeout=5
x-powered-by: Express
{"xabar":"salom","yol":"/salom"}
404 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot PUT /salom</pre>
</body>
</html>

Besh qatorni o'zimiz yozmadik, Express o'zi qo'ydi:

  1. content-type: application/json; charset=utf-8 va content-length — res.json() ishi.
  2. etag: W/"20-…" — Express har javobga ETag hisoblaydi: 20 — uzunlik (32 bayt, o'n oltilikda), keyin tana hash'i. Static fayl darsidagi ETag'ga o'xshash, lekin mazmundan. Kanonda bu hatto 404 va 422 xato javoblarida ham chiqdi.
  3. x-powered-by: Express — "men Express'man". Hujumchiga foydali ma'lumot: qaysi zaifliklarni sinashni bildiradi. app.disable("x-powered-by") bilan o'chiriladi.
  4. 404 HTML sahifa — Cannot PUT /salom. PUT /salom uchun marshrut yo'q, Express esa yo'l bor-yo'qligini bilmaydi va 404 beradi. To'g'risi — 405 Method Not Allowed + Allow sarlavhasi (qo'lda routing darsi). Express 405 ni o'zi bermaydi — kanon testlari buni topdi, yechim middleware darsida.
  5. HEAD — biz faqat app.get yozdik, lekin HEAD /salom ham ishlaydi: Express GET ishlovchisini chaqiradi va tanani tashlaydi (biz sinadik: 200, content-length: 32).

Diqqat: HTML 404 sahifasi JSON API uchun noqulay: frontend res.json() qilganda SyntaxError: Unexpected token '<' oladi (fetch darsi). O'z API'ingizda oxirgi middleware sifatida JSON 404 qo'ying — «API qadami» bo'limida ko'rasiz.

5. Narxi: o'lchov

Framework bepul emas. Kanon holat 07 (node:http) va holat 13 (Express) ni bir xil sharoitda o'lchadi — o'sha mashina, o'sha so'rovlar. Server va yuk beruvchi (autocannon, 10 ulanish × 5 s) bitta kompyuterda, bitta o'lchov:

GET /vazifalar/5 — bitta kichik vazifa
  • node:http (holat 07)27 096 req/s
  • Express 5.3.0 (holat 13)9 724 req/s

Manba: Kanon o'lchovi, autocannon 8.0.0, 10 ulanish × 5 s, i5-12500H, Node 24.21.0, bitta mashina, 2026-10-24

Kichik javobda Express ~2.8 marta sekinroq: har so'rov middleware zanjiri, query tahlili, ETag hisoblashdan o'tadi. Katta javobda esa farq yo'qoladi. GET /vazifalar (100 vazifa, 26 KB) — 2 669 va 3 237 req/s. Vaqtning asosiy qismi JSON.stringify va tarmoqqa ketadi, Express qatlami sezilmaydi. Ikkinchi raqam hatto katta — bu shovqin, bitta o'lchovdan xulosa chiqarmang.

Sovuq start (jarayon boshlanishidan "server tayyor" qatorigacha, 10 marta mediana): 171 → 289 ms. Taxminan 118 ms — Express va uning 65 paketini import qilish.

Xulosa: bu narx deyarli har doim to'lanadi. Haqiqiy API'da so'rovning asosiy vaqti bazaga ketadi (millisekundlar), Express qatlami esa mikrosekundlar. Lekin "framework tekin" degan fikr noto'g'ri — biz uni raqam bilan ko'rdik.

5.1 Muqobillar

Framework Xususiyati
Express eng keng tarqalgan, minimal, ekotizimi ulkan
Fastify tezlikka qaratilgan, JSON Schema bilan validatsiya
Koa Express mualliflaridan, async middleware
Hono Web standartlari (Request/Response), Node, Bun, Deno, Cloudflare Workers
NestJS Express yoki Fastify ustida to'liq arxitektura (modul, DI)

Ularni kanonda o'lchamadik — shuning uchun tezlik raqamlari yo'q. NestJS'ni alohida qismda (30-qism) o'rganamiz. Express bilimi hammasiga ko'chadi: so'rov, javob, middleware, marshrut tushunchalari bir xil.

Tekshirib ko'ring: Hamkasbingiz "Express sekin, Fastify'ga o'tamiz" dedi. API'ning har so'rovi bazaga 20 ms ketadi. Nima deysiz?

Javob

Avval o'lchash kerak. Bizning o'lchovda Express qatlami bitta so'rovga taxminan 0.07 ms qo'shadi (1/9 724 − 1/27 096 soniya). 20 ms bazaga nisbatan bu ~0.3 %. Fastify'ga ko'chish bu yerda deyarli foyda bermaydi. Tezlik muammosi bo'lsa — avval bazani optimallashtirish kerak.

6. API qadami: Express'ga ko'chish (holat 13, 1-qism)

Holat 13 besh darsga bo'lingan: bugun Express'ning o'zi, keyin req, res, middleware va Router va qatlamlar. Hali commit qilmang — commit beshinchi darsda.

Bugun loyihani bir qadamda Express'ga o'tkazamiz va testlarni yashil qilamiz. Kod keyingi uch darsda o'zgarmaydi — biz uni qism-qism ochib chiqamiz. Beshinchi darsda esa marshrutlar alohida fayllar va qatlamlarga bo'linadi (kanon holat 13). Bugungi kesim kanonda alohida holat emas: biz uni holat 07 va 13 orasida yig'ib, tekshirdik (Node 24.21.0: tsc 0, ESLint 0, testlar 7/7).

1. O'rnatish — «O'rnatish» bo'limidagi ikki buyruq. package.json da "dependencies": { "express": "5.3.0" } paydo bo'ladi — loyihaning birinchi prod bog'liqligi.

2. O'chiriladigan fayllar. src/statik.ts — uning ishini express.static qiladi: MIME, ETag, Last-Modified, Range/206, 304, traversal himoyasi. src/tana.ts — jsonTana o'rniga express.json({ limit: "16kb" }). HttpXato klassi yangi faylga ko'chadi.

3. Yangi src/umumiy/http-xato.ts (kanon):

ts
// http-xato.ts — status kodli xato (#23 da Problem Details,
// #32 da iyerarxiya bo'ladi)
export class HttpXato extends Error {
  readonly holat: number;
  constructor(holat: number, xabar: string) {
    super(xabar);
    this.name = "HttpXato";
    this.holat = holat;
  }
}

4. Yangi src/umumiy/json-kerak.ts (kanon; nega kerakligi — req darsida):

ts
// json-kerak.ts — marshrut darajasidagi middleware: tanali so'rov
// faqat JSON bo'lsin (415)
import type { RequestHandler } from "express";
import { HttpXato } from "./http-xato.ts";

// Express 5: Content-Type JSON bo'lmasa express.json() tanani
// o'qimaydi va req.body = undefined — xato YO'Q. Shuning uchun
// turini o'zimiz tekshiramiz (req.is: JSON — "application/json",
// boshqa — false, tana yo'q — null)
export const jsonKerak: RequestHandler = (req, _res, next) => {
  if (req.is("application/json") !== "application/json")
    throw new HttpXato(415,
      "Content-Type: application/json bo'lishi kerak");
  next();
};

5. src/ilova.ts — to'liq yangi fayl. src/ombor.ts va src/vazifa.ts holat 07 dagidek qoladi (sinxron XotiraOmbori):

ts
// ilova.ts — Express ilovasi (oraliq holat: marshrutlar shu
// faylda, #13 da Router va qatlamlarga bo'linadi).
// listen() YO'Q — u ishga-tushir.ts da
import express from "express";
import type { ErrorRequestHandler, RequestHandler } from "express";
import { join } from "node:path";
import { HttpXato } from "./umumiy/http-xato.ts";
import { jsonKerak } from "./umumiy/json-kerak.ts";
import { ozgarishniTekshir, yangiVazifaniTekshir } from "./vazifa.ts";
import type { XotiraOmbori } from "./ombor.ts";

export type Bogliqliklar = { ombor: XotiraOmbori };

function idniOl(xom: string): number {
  if (!/^[1-9]\d{0,9}$/.test(xom)) {
    throw new HttpXato(400, "id — musbat butun son");
  }
  return Number(xom);
}

// Express metod mos kelmasa 404 beradi — 405 + Allow ni o'zimiz
function metodYoq(allow: string): RequestHandler {
  return (req, res) => {
    res.set("Allow", allow);
    throw new HttpXato(405, `${req.method} bu manzilda yo'q`);
  };
}

function topilmadi(id: number): HttpXato {
  return new HttpXato(404, `Vazifa ${String(id)} topilmadi`);
}

const xatoIshlovchi: ErrorRequestHandler = (
  xato: unknown, _req, res, next,
) => {
  if (res.headersSent) {
    next(xato); // javob yarmida — Express o'zi ulanishni uzadi
    return;
  }
  if (xato instanceof HttpXato) {
    res.status(xato.holat).json({ xato: xato.message });
    return;
  }
  // body-parser xatolari: `type` maydoni bilan (http-errors)
  const tur =
    typeof xato === "object" && xato !== null && "type" in xato
      ? xato.type
      : undefined;
  if (tur === "entity.parse.failed") {
    res.status(400).json({ xato: "Tana yaroqli JSON emas" });
    return;
  }
  if (tur === "entity.too.large") {
    res.status(413).json({ xato: "Tana 16 KB dan katta" });
    return;
  }
  // Boshqa kutubxonalar (serve-static: 416, 403...) http-errors
  // tashlaydi: status + expose
  if (
    typeof xato === "object" && xato !== null
    && "status" in xato && typeof xato.status === "number"
    && xato.status < 500
    && "expose" in xato && xato.expose === true
    && xato instanceof Error
  ) {
    res.status(xato.status).json({ xato: xato.message });
    return;
  }
  console.error(xato);
  res.status(500).json({ xato: "Server xatosi" });
};

export function ilovaYarat({ ombor }: Bogliqliklar): express.Express {
  const app = express();
  app.disable("x-powered-by"); // "Express" ekanini oshkor qilmaymiz
  app.use(express.static(join(import.meta.dirname, "..", "public")));
  app.use(express.json({ limit: "16kb" }));

  app.route("/vazifalar")
    .get((req, res) => {
      const b = req.query.bajarildi;
      if (b !== undefined && b !== "true" && b !== "false") {
        throw new HttpXato(400, "bajarildi — true yoki false");
      }
      const royxat = ombor.royxat();
      res.json(b === undefined
        ? royxat
        : royxat.filter((v) => v.bajarildi === (b === "true")));
    })
    .post(jsonKerak, (req, res) => {
      const t = yangiVazifaniTekshir(req.body);
      if (!t.ok) throw new HttpXato(422, t.xato);
      const v = ombor.yarat(t.qiymat);
      res.status(201).location(`/vazifalar/${String(v.id)}`).json(v);
    })
    .all(metodYoq("GET, HEAD, POST"));

  app.route("/vazifalar/:id")
    .get((req, res) => {
      const id = idniOl(req.params.id);
      const v = ombor.ol(id);
      if (!v) throw topilmadi(id);
      res.json(v);
    })
    .patch(jsonKerak, (req, res) => {
      const id = idniOl(req.params.id);
      const t = ozgarishniTekshir(req.body);
      if (!t.ok) throw new HttpXato(422, t.xato);
      const v = ombor.yangila(id, t.qiymat);
      if (!v) throw topilmadi(id);
      res.json(v);
    })
    .delete((req, res) => {
      const id = idniOl(req.params.id);
      if (!ombor.ochir(id)) throw topilmadi(id);
      res.sendStatus(204);
    })
    .all(metodYoq("GET, HEAD, PATCH, DELETE"));

  app.use((req) => {
    throw new HttpXato(404, `${req.method} ${req.path} topilmadi`);
  });
  app.use(xatoIshlovchi);
  return app;
}

Bu fayl — keyingi uch darsning "xaritasi": req.params, req.query, req.body, jsonKerak — req darsida; res.status().location().json(), sendStatus — res darsida; tartib, 404, xatoIshlovchi, metodYoq — middleware darsida. Hozir tuzilmaga qarang: static → JSON tana → marshrutlar → JSON 404 → xato ishlovchi. ilovaYarat da listen yo'q — testlar ilovani istalgan portda (0) ishga tushira oladi.

Express app.use va app.route larni yozilgan tartibda sinaydi. PATCH /vazifalar/7 so'rovi {"bajarildi":true} tanasi bilan shu yo'ldan o'tadi:

Qadam Middleware Natija
1 express.static faqat GET/HEAD ga fayl beradi; PATCH — next()
2 express.json tana o'qildi: req.body = { bajarildi: true }
3 /vazifalar marshruti yo'l mos emas — o'tkazib yuboriladi
4 /vazifalar/:id → jsonKerak Content-Type JSON — next()
5 .patch ishlovchisi vazifa topildi — res.json(v), 200; zanjir shu yerda tugaydi

Agar 5-qadamda vazifa topilmasa, ishlovchi throw qiladi. Express qolgan oddiy ishlovchilarni (.all, JSON 404) tashlab, to'g'ridan oxirgi xatoIshlovchi ga o'tadi — u 404 JSON qaytaradi. JSON 404 ishlovchisiga esa faqat hech bir marshrut mos kelmagan so'rov yetadi.

6. src/ishga-tushir.ts — createServer ga endi Express ilovasi beriladi. Timeout'lar holat 07 dagidek. Kanon holat 13 fayli (bugun bitta farq bilan: XotiraOmbori hali ./ombor.ts dan):

ts
// ishga-tushir.ts — http server (Express ilovasi ishlovchi
// sifatida), timeout'lar va tinglash
import { createServer } from "node:http";
import type { Server } from "node:http";
import type { AddressInfo } from "node:net";
import { ilovaYarat } from "./ilova.ts";
import type { Bogliqliklar } from "./ilova.ts";
import { XotiraOmbori } from "./ombor.ts";

export type Ishlayotgan = { server: Server; manzil: string };

type Sozlama = { port?: number; host?: string;
  bogliqliklar?: Bogliqliklar };

export async function ishgaTushir({
  port = 0, host = "127.0.0.1",
  bogliqliklar = { ombor: new XotiraOmbori() },
}: Sozlama = {}): Promise<Ishlayotgan> {
  // app.listen() ham shuni qiladi; createServer — timeout'larni
  // berish va server obyektini olish uchun
  const server = createServer(
    {
      // LB (nginx, ALB) bo'sh ulanishni ~60 s ushlaydi — Node undan
      // KEYIN yopsin
      keepAliveTimeout: 65_000,
      // Slowloris: sarlavhalar 10 s ichida kelmasa — 408
      headersTimeout: 10_000,
      requestTimeout: 30_000,
    },
    ilovaYarat(bogliqliklar),
  );
  await new Promise<void>((resolve, reject) => {
    server.once("error", reject); // EADDRINUSE va h.k.
    server.listen(port, host, () => {
      server.off("error", reject);
      resolve();
    });
  });
  const { port: haqiqiy } = server.address() as AddressInfo;
  return { server, manzil: `http://${host}:${String(haqiqiy)}` };
}

(Qatorlar telefon uchun bo'lingan, kod ma'nosi aynan.) app.listen() emas, createServer — timeout'larni berish va server obyektini yop() ga uzatish uchun.

7. Testlar. Ikki o'zgarish. (1) test/api.test.ts da allow endi "GET, HEAD, POST" — Express HEAD ni o'zi qo'llaydi. (2) Static fayl testi fetch bilan 304 olmaydi — fetch ETag'li so'rovga o'zi Cache-Control: no-cache qo'shadi va Express 200 beradi (sababi req darsida). Shartli so'rovlar uchun test/yordamchi.ts ga node:http bilan so'rov yuboradigan yordamchi qo'shamiz (kanon):

ts
import { request } from "node:http";
import type { IncomingHttpHeaders } from "node:http";

export type XomJavob = { holat: number;
  sarlavhalar: IncomingHttpHeaders; tana: string };

/**
 * node:http bilan so'rov. Nega fetch emas: fetch (undici)
 * If-None-Match bor so'rovga o'zi `Cache-Control: no-cache` +
 * `Pragma: no-cache` qo'shadi (Fetch standarti) — Express (fresh)
 * esa no-cache'li so'rovga 304 bermaydi.
 */
export function xomSorov(url: string, { metod = "GET",
  sarlavhalar = {}, tana }: { metod?: string;
  sarlavhalar?: Record<string, string>; tana?: string } = {},
): Promise<XomJavob> {
  return new Promise((resolve, reject) => {
    const so = request(url, { method: metod, headers: sarlavhalar },
      (res) => {
        let b = "";
        res.setEncoding("utf8");
        res.on("data", (d: string) => (b += d));
        res.on("end", () => {
          resolve({ holat: res.statusCode ?? 0,
            sarlavhalar: res.headers, tana: b });
        });
      });
    so.on("error", reject);
    so.end(tana);
  });
}

Static test esa shunday bo'ladi (kanon):

ts
test("statik (express.static): index.html, ETag → 304, "
  + "Range → 206, traversal → 404", async () => {
  const r = await xomSorov(`${asos}/`);
  assert.equal(r.holat, 200);
  assert.match(String(r.sarlavhalar["content-type"]), /text\/html/);
  const etag = String(r.sarlavhalar.etag);
  assert.equal((await xomSorov(`${asos}/`,
    { sarlavhalar: { "If-None-Match": etag } })).holat, 304);
  const q = await xomSorov(`${asos}/index.html`,
    { sarlavhalar: { Range: "bytes=0-14" } });
  assert.equal(q.holat, 206);
  assert.equal(q.tana, "<!doctype html>");
  assert.equal((await xomSorov(`${asos}/index.html`,
    { sarlavhalar: { Range: "bytes=99999-" } })).holat, 416);
  // node:http yo'lni normallashtirmaydi — server o'zi
  // himoyalanishi kerak
  assert.equal(
    (await xomSorov(`${asos}/../package.json`)).holat, 404);
  assert.equal(
    (await xomSorov(`${asos}/..%2fpackage.json`)).holat, 404);
});

npm run check — tip 0, lint 0, testlar 7/7. Bu — eng yaxshi isbot: holat 07 dagi API shartnomasi (statuslar, JSON shakli) Express'da ham buzilmadi.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
SyntaxError: Cannot use import statement outside a module package.json da "type": "module" yo'q qo'shing yoki .mjs
Cannot find module 'express' o'rnatilmagan npm i express
PUT ga 404 HTML, 405 emas Express 405 bermaydi .all() + Allow (middleware darsi)
Javobda X-Powered-By: Express sukut app.disable("x-powered-by")
Internetdagi misol ishlamaydi (/*) Express 4 sintaksisi Express 5 shakli (Express 5 darsi)
app.listen timeout'larni bermaydi Express sozlamasi emas createServer(sozlama, app)

Cannot find module 'express' imported from … — "express moduli topilmadi". node_modules yo'q yoki boshqa papkada ishga tushirdingiz.

8. Mashqlar

Yechimlar kurs/mashqlar/25/09-express-kirish/ da.

1-mashq (oson): to'ldiring

  • express() qaytaradigan qiymat turi —
  • Express 5 talab qiladigan eng past Node versiyasi —
  • X-Powered-By ni o'chirish — app.("x-powered-by")
  • Marshrut yo'q metodga Express qaytaradigan status —
  • app.listen qaytaradigan obyekt — http.
Yechim
  • function — (req, res, next), createServer ga beriladi.
  • 18 — "engines": { "node": ">= 18" }.
  • disable — app.set("x-powered-by", false) ham bir xil.
  • 404 — 405 ni o'zingiz qo'yasiz.
  • Server — timeout'lar va close() shu obyektda.

2-mashq (o'rta): o'z "kichik Express"ingiz

Express — yupqa qatlam ekanini isbotlang. kichikIlova() funksiyasini faqat node:http bilan yozing: u funksiya qaytarsin (ishlovchi), unda get(yol, fn) va post(yol, fn) metodlari va listen(...) bo'lsin. Har so'rovda res.json(qiymat) yordamchisi qo'shilsin. Marshrut topilmasa — 404 va Cannot <METOD> <yol>. Eslatma: funksiya ham obyekt, unga xususiyat qo'shish mumkin (funksiya obyekt sifatida).

Yechim
js
import { createServer } from "node:http";

function kichikIlova() {
  const yollar = [];
  const ilova = (req, res) => {
    const yol = new URL(req.url, "http://x").pathname;
    const mos = yollar.find(
      (y) => y.metod === req.method && y.yol === yol);
    res.json = (qiymat) => {
      res.setHeader("Content-Type", "application/json");
      res.end(JSON.stringify(qiymat));
    };
    if (!mos) {
      res.statusCode = 404;
      return res.end(`Cannot ${req.method} ${yol}`);
    }
    mos.ishlovchi(req, res);
  };
  for (const metod of ["GET", "POST"]) {
    ilova[metod.toLowerCase()] = (yol, ishlovchi) =>
      yollar.push({ metod, yol, ishlovchi });
  }
  ilova.listen = (...args) => createServer(ilova).listen(...args);
  return ilova;
}

const app = kichikIlova();
app.get("/salom", (req, res) => res.json({ xabar: "salom" }));

const server = app.listen(0, "127.0.0.1");
await new Promise((ok) => server.once("listening", ok));
const a = `http://127.0.0.1:${server.address().port}`;
const r1 = await fetch(`${a}/salom`);
const tur = r1.headers.get("content-type");
console.log(r1.status, tur, await r1.text());
const r2 = await fetch(`${a}/salom`, { method: "PUT" });
console.log(r2.status, await r2.text());
server.closeAllConnections();
server.close();

Konsolda:

text
200 application/json {"xabar":"salom"}
404 Cannot PUT /salom

30 qatorda Express'ning asosiy g'oyasi: ilova — funksiya, marshrutlar — ro'yxat, res ga yordamchi metodlar qo'shiladi. Haqiqiy Express bunga middleware zanjiri, yo'l parametrlari va xato ishlashni qo'shadi.

3-mashq (qiyin): 405 bilan Express

Express 5.3.0 bilan «Bahor» oshxonasi uchun /taomlar yozing: GET — ro'yxat, POST — JSON tanadan yangi taom (201). Boshqa metodlar — 405, Allow: GET, HEAD, POST va JSON xabar. X-Powered-By bo'lmasin. Eslatma: app.route("/taomlar").get(…).post(…).all(…) — bitta yo'lga bir nechta metod; .all — qolgan hamma metod; express.json() — JSON tanani req.body ga o'qiydi (tafsiloti req darsida). npm i express@5.3.0 dan keyin ishga tushiring.

Yechim
js
import express from "express";

const taomlar = [{ nom: "osh", narx: 35000 }];
const app = express();
app.disable("x-powered-by");
app.use(express.json());
app.route("/taomlar")
  .get((req, res) => res.json(taomlar))
  .post((req, res) => {
    taomlar.push(req.body);
    res.status(201).json(req.body);
  })
  .all((req, res) => {
    res.set("Allow", "GET, HEAD, POST");
    res.status(405).json({ xato: `${req.method} mumkin emas` });
  });

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}/taomlar`;

const yangi = { nom: "manti", narx: 30000 };
const r1 = await fetch(url, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(yangi),
});
console.log(r1.status, await r1.text());
const r2 = await fetch(url);
console.log(r2.status, await r2.text());
const r3 = await fetch(url, { method: "DELETE" });
console.log(r3.status, r3.headers.get("allow"), await r3.text());
console.log("x-powered-by:", r3.headers.get("x-powered-by"));
server.closeAllConnections();
server.close();

Konsolda:

text
201 {"nom":"manti","narx":30000}
200 [{"nom":"osh","narx":35000},{"nom":"manti","narx":30000}]
405 GET, HEAD, POST {"xato":"DELETE mumkin emas"}
x-powered-by: null

.all() zanjirning oxirida — GET va POST undan oldin ushlanadi, unga faqat qolganlari yetadi. Tartibni almashtirsangiz, hamma so'rov 405 oladi. Validatsiya yo'q (req.body tekshirilmaydi) — bu validatsiya darsida.

4-mashq: API qadami

«API qadami» bo'limidagi 1–7 qadamlarni bajaring. Avval testlarni o'zgartirmasdan npm test qiling — qaysi test yiqilishini va nega ekanini yozib qo'ying. Keyin 7-qadam (xomSorov, Allow) bilan tuzating: npm run check — 7/7. Commit qilmang.

Yechim

To'liq fayllar — «API qadami» bo'limida. Testlarni o'zgartirmasdan ikki tekshiruv yiqiladi: allow — "GET, HEAD, POST" keldi ("GET, POST" kutilgan edi), static testida fetch + If-None-Match 304 o'rniga 200 oldi. Ikkinchisining sababi — undici bunday so'rovga Cache-Control: no-cache qo'shadi (req darsi).

9. Real ishda

  • Express — Node backend'larining katta qismida; NestJS ham sukut bo'yicha ichida Express ishlatadi. Vakansiyalarda "Node.js/Express" juftligi juda ko'p uchraydi.
  • Eski loyihalar — ko'pchiligi hali Express 4 da. 4 → 5 ko'chish — real ish vazifasi (Express 5 darsi).
  • Arxitektura odati: ilovani yaratish (ilovaYarat) va tinglash (listen) alohida — testlar, serverless platformalar va graceful shutdown uchun.
  • Intervyu: "Express nima, http dan farqi?", "app.listen ichida nima bor?", "Express 5 da nima o'zgardi?".

Xulosa

  • Express — node:http ustidagi yupqa qatlam: routing + middleware; app — (req, res, next) funksiyasi.
  • app.listen() = http.createServer(app).listen(); timeout'lar va close() — http.Server da.
  • Express 5.3.0, Node 18+, ESM; eski maqolalar ko'pincha Express 4 uchun.
  • Sukut: X-Powered-By (o'chiring), har javobga ETag, HTML 404, avtomatik HEAD, 405 YO'Q.
  • Narxi: 65 paket, +118 ms sovuq start, kichik javobda 27 096 → 9 724 req/s; katta javobda farq yo'qoladi.

Keyingi dars: req obyekti — params, query (Express 5 getter), body, req.is(), req.ip va express.json() ning jim tuzog'i.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Express.js'ga kirish: Express 5, app.get va app.listen, node:http ustidagi yupqa qatlam — IlmHamroh