IlmHamroh
JavaScript Full-stack/29-qism. Backend integratsiyalar: fayl, to'lov, bot, realtime63/69-dars19 daqiqa
Mundarija (31)

Fastify: sxemaga asoslangan tez Node.js freymvorki

Qisqacha: Fastify — Express o'rnini bosa oladigan Node.js freymvorki. Har marshrutga JSON Schema berasiz: kiruvchi tana shu sxema bo'yicha tekshiriladi, javob esa sxema bo'yicha tez satrga aylantiriladi. Kod plaginlarga bo'linadi va har plagin o'z "qutisida" yashaydi (encapsulation). Kanon o'lchovida oddiy GET'da Fastify Express'dan qariyb ikki baravar ko'p so'rov o'tkazdi.

Bu darsda

  • Fastify'da marshrut yozib, uni portsiz inject() bilan sinaysiz.
  • JSON Schema bilan tanani tekshirasiz va Fastify'ning sukut sozlamasidagi ikki tuzoqni (ortiqcha maydonni jim o'chirish, turni o'zgartirish) bilasiz.
  • Javob sxemasi maxfiy maydonni qanday "kesib" tashlashini ko'rasiz.
  • Plagin, decorate, fastify-plugin va hooks tartibini tushuntira olasiz.
  • Qachon Express'da qolish, qachon Fastify'ga o'tish kerakligini o'lchov bilan asoslaysiz.

Oldin bilishingiz kerak: Express'ga kirish va Express 5, Middleware va request lifecycle, Validatsiya: nima uchun, qayerda va qanday, Pino chuqur, Supertest bilan HTTP integration test.

Oldingi darsda servislar orasidagi tez aloqa — gRPC ni ko'rdik. Endi qismning oxirgi bloki: Express'dan tashqaridagi freymvorklar va runtime'lar. Birinchisi — Fastify.

1. Nega bu kerak?

vazifalar-api Express 5 da yozilgan va yaxshi ishlaydi. Unda Zod bilan validatsiya, Pino bilan log, Problem Details bilan xatolar bor. Bularning hammasini o'zimiz middleware qilib ulaganmiz.

Fastify boshqa yo'ldan boradi. Validatsiya, log va javobni satrga aylantirish uning ichida bor. Siz faqat sxema yozasiz. Bundan ikki foyda keladi:

  1. Tezlik. Fastify sxemani oldindan "kompilyatsiya" qiladi: tekshiruvchi va JSON yozuvchi funksiya bir marta yasaladi, keyin har so'rovda tayyor ishlatiladi.
  2. Tartib. Har marshrutning kirishi va chiqishi sxemada yozilgan. Bu sxemadan hujjat (OpenAPI) ham yasaladi.

Kanon bir xil API'ni (GET JSON va POST validatsiya bilan) sakkizta server bilan yozib, autocannon bilan o'lchadi. Raqamlarga qarang:

GET /v1/salom: soniyasiga so'rov
  • Express 5.3.0p99 25 ms10 698 req/s
  • Koa 3.2.1p99 12 ms14 457 req/s
  • Fastify 5.12.7p99 7 ms20 514 req/s
  • Hono (Node)p99 7 ms21 634 req/s
  • Bun.servep99 5 ms30 430 req/s
  • Deno.servep99 16 ms9 122 req/s

Manba: IlmHamroh kanon o'lchovi, 2026-10-27: autocannon 8.0.0, 50 ulanish, 5 s, Node 24.21.0, Windows 11, i5-12500H; bitta mashinada, taqqoslash uchun

Fastify: 20 514 so'rov/s, Express: 10 698. POST'da (validatsiya bilan) farq yanada katta: 18 217 va 8 236.

Lekin halol izoh shart. Bu — bo'sh API: bazaga murojaat yo'q. 25-qismda Express bazaga ulangan holda 9 724 so'rov/s bergan edi. Haqiqiy API'da vaqtning ko'pi bazada o'tadi, freymvorklar farqi kichrayadi. O'lchovlar orasida ±10 % tebranish ham bor (kanon ikki marta ishga tushirgan). Shuning uchun "Fastify'ga o'tsak, sayt ikki baravar tezlashadi" — noto'g'ri xulosa.

O'xshatish: Express — oddiy oshxona, har oshpaz idishni o'zi tanlaydi. Fastify — har taom uchun tayyor texnologik karta bor oshxona: kartani bir marta yozasiz, keyin hammasi tez va bir xil.

2. Birinchi server va inject()

2.1 O'rnatish va marshrut

bash
npm i fastify@5.12.7

Kanon versiyasi — Fastify 5.12.7 (Node 24.21.0). Fastify 5 Node 20 va undan yangisini talab qiladi (rasmiy "Migration Guide V5").

js
import Fastify from "fastify";

const app = Fastify({ logger: true });

app.get("/v1/salom", async () => {
  return { salom: "dunyo" };
});

await app.listen({ port: 3000, host: "127.0.0.1" });

Express'dan farqlar:

  • async funksiya qaytargan qiymat — javob. res.json() chaqirmaysiz.
  • logger: true — ichidagi Pino yoqiladi. Har so'rov bitta JSON qator bo'lib logga tushadi (Pino chuqur darsidagi format). Bu qatorlarda vaqt, pid va reqId bor — har safar boshqacha, shuning uchun bu yerda chiqishini bermaymiz.
  • Marshrut ichidagi throw avtomatik 500 javobga aylanadi (Express 5 dagidek).

2.2 Portsiz test: inject()

Supertest darsida ilovani port ochmasdan sinagandik. Fastify'da bu imkoniyat ichida bor: app.inject() so'rovni tarmoqsiz, to'g'ridan marshrutga yuboradi. Ichida light-my-request kutubxonasi ishlaydi. Bu darsdagi barcha tajribalar shu usulda — port, vaqt, Date sarlavhasi yo'q, chiqish barqaror.

js
const r = await app.inject({ method: "GET", url: "/v1/salom" });
console.log(r.statusCode, r.body); // 200 {"salom":"dunyo"}

Maslahat: inject() birinchi chaqirilganda ilova "ishga tushgan" holatga o'tadi. Shundan keyin addHook yoki yangi marshrut qo'shib bo'lmaydi — biz sinab ko'rdik: FastifyError: Fastify instance is already listening. Cannot call "addHook"!. Testda har holat uchun yangi Fastify() nusxasi yasang.

3. JSON Schema bilan validatsiya

3.1 Tana sxemasi

JSON Schema — JSON ma'lumot qanday bo'lishi kerakligini JSON'ning o'zi bilan yozish standarti. Zod'da z.object({ matn: z.string().min(1) }) yozardingiz. JSON Schema'da xuddi shu narsa obyekt bo'lib yoziladi. Fastify uni Ajv kutubxonasi bilan tekshiradi — JavaScript'dagi eng ko'p ishlatiladigan JSON Schema tekshiruvchisi.

Biz ishga tushirdik (Node 24.21.0, fastify 5.12.7):

js
import Fastify from "fastify";

const app = Fastify();
const Vazifa = {
  type: "object",
  required: ["matn"],
  additionalProperties: false,
  properties: {
    matn: { type: "string", minLength: 1, maxLength: 100 },
  },
};

app.post("/v1/vazifalar", { schema: { body: Vazifa } },
  async (req, reply) => {
    reply.code(201);
    return { id: 1, matn: req.body.matn };
  });

const yubor = (payload) => app.inject({
  method: "POST", url: "/v1/vazifalar", payload,
});
for (const tana of [
  { matn: "Non olish" },
  { matn: "" },
  { qoshimcha: 1 },
]) {
  const r = await yubor(tana);
  console.log(r.statusCode, r.body);
}

Konsolda:

text
201 {"id":1,"matn":"Non olish"}
400 {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"body/matn must NOT have fewer than 1 characters"}
400 {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"body must have required property 'matn'"}

Marshrut funksiyasi faqat birinchi so'rovda chaqirildi. Qolgan ikkitasini Fastify o'zi 400 bilan qaytardi. Xabarlar inglizcha: "body/matn must NOT have fewer than 1 characters" — "tanadagi matn 1 belgidan kam bo'lmasligi kerak"; "body must have required property 'matn'" — "tanada majburiy matn maydoni bo'lishi kerak".

payload ga obyekt bersangiz, inject() uni JSON qilib, content-type: application/json qo'yadi. Buzuq JSON yuborsangiz boshqa kod chiqadi — biz sinadik: 400 FST_ERR_CTP_INVALID_JSON_BODY ("Body is not valid JSON but content-type is set to 'application/json'"). Yo'q marshrut — 404 {"message":"Route GET:/yoq not found",…}.

3.2 Ikki tuzoq: sukut Ajv sozlamasi

Endi hujumchi kabi o'ylaymiz. Tanaga ortiqcha maydon qo'shamiz va matn ga son beramiz:

js
for (const tana of [
  { matn: "Non", admin: true },
  { matn: 42 },
]) {
  const r = await yubor(tana);
  console.log(r.statusCode, r.body);
}

Biz ishga tushirdik — natija kutilganidek emas:

text
201 {"id":1,"matn":"Non"}
201 {"id":1,"matn":"42"}

Ikkalasi ham o'tdi:

  1. additionalProperties: false bo'lsa ham admin uchun xato yo'q. Fastify uni jim o'chirib tashladi.
  2. 42 soni "42" satriga aylandi va qabul qilindi.

Sababi — Fastify Ajv'ni o'z sukut sozlamasi bilan ishga tushiradi. Rasmiy "Validation and Serialization" hujjatida (2026-10-28 da tekshirildi) shunday yozilgan:

Sozlama Sukut Ma'nosi
removeAdditional true additionalProperties: false bo'lsa ortiqcha maydon o'chiriladi
coerceTypes "array" tur sxemaga moslab o'zgartiriladi
useDefaults true default qiymatlar qo'yiladi
allErrors false birinchi xatoda to'xtaydi (DoS — serverni band qilib qo'yish hujumidan himoya)

Bu xato emas, ataylab tanlangan sozlama. Query parametrlar uchun qulay ham: ?sahifa=2 satr bo'lib keladi, sxemada esa integer. Lekin JSON tana uchun bu "jim" xatti-harakat chalkashtiradi. Mijoz admin: true yuborib, server buni qabul qildi deb o'ylaydi.

Diqqat: Kanon jadvalida Fastify ustunida "400 FST_ERR_VALIDATION, additionalProperties:false" yozilgan. Kanon so'rovida matn bo'sh edi — 400 aynan shu minLength uchun chiqqan, ortiqcha maydon uchun emas. Biz buni alohida sinab ko'rdik: ortiqcha maydon sukutda jim o'chiriladi.

3.3 Qat'iy rejim

Sozlamani ajv.customOptions orqali o'zgartirasiz:

js
const app = Fastify({
  ajv: {
    customOptions: { removeAdditional: false, coerceTypes: false },
  },
});

Shu sozlama bilan o'sha uch so'rov (biz ishga tushirdik, xato ishlovchi «Xatolarni Problem Details'ga aylantirish» bo'limidagidek):

text
201 {"id":1,"matn":"Non olish"}
400 {"type":"about:blank","title":"Validatsiya xatosi","status":400,"detail":"body must NOT have additional properties"}
400 {"type":"about:blank","title":"Validatsiya xatosi","status":400,"detail":"body/matn must be string"}

Endi admin ham, 42 ham rad etildi. Qaysi rejim to'g'ri — loyihaga bog'liq. Muhimi: sukutni bilib tanlang.

Tekshirib ko'ring: GET /v1/vazifalar?sahifa=2 marshrutida querystring sxemasi sahifa: { type: "integer" }. coerceTypes: false qilsangiz nima bo'ladi?

Javob

Query'dagi har qiymat — satr ("2"). Tur o'zgartirish o'chiq bo'lsa, "2" integer emas — 400 xato chiqadi. Shuning uchun ba'zi jamoalar sukutni saqlaydi va faqat tana sxemalarida qat'iylik qiladi. Ajv'ni marshrut bo'yicha ham sozlash mumkin, lekin oddiy yo'l — sukutni qoldirib, xatti-harakatini bilish.

4. Javob sxemasi: serializatsiya

4.1 Maxfiy maydon kesiladi

Serializatsiya (serialization) — obyektni tarmoqqa yuboriladigan satrga (bu yerda JSON'ga) aylantirish. Express JSON.stringify ni chaqiradi va obyektdagi hamma narsani yuboradi. Fastify'da javob sxemasi bersangiz, fast-json-stringify kutubxonasi faqat sxemadagi maydonlarni yozadi.

Ataylab xato qilamiz — marshrut bazadan kelgan foydalanuvchi obyektini parol hash'i bilan qaytaradi:

js
const Javob = {
  type: "object",
  properties: {
    id: { type: "integer" },
    matn: { type: "string" },
  },
};

app.post("/v1/vazifalar", {
  schema: { body: Vazifa, response: { 201: Javob } },
}, async (req, reply) => {
  reply.code(201);
  return { id: 1, matn: req.body.matn, parolHash: "$2b$12$maxfiy" };
});

Biz ishga tushirdik: javob — 201 {"id":1,"matn":"Non olish"}. parolHash mijozga chiqmadi. Rasmiy hujjat buni ochiq aytadi: javob sxemasi maxfiy ma'lumotning tasodifan oqib ketishini oldini olishga yordam beradi va o'tkazuvchanlikni oshiradi.

Bu — mass assignment ning teskarisi: u yerda ortiqcha maydon kirib kelardi, bu yerda ortiqcha maydon chiqib ketardi.

4.2 Lekin bu yagona himoya emas

Javob sxemasi — xavfsizlik to'ri, asosiy himoya emas. Sxemasiz marshrut (masalan, yangi qo'shilgan) hamma narsani yuboradi. Bazadan faqat kerakli ustunlarni tanlash (Prisma da select) — birinchi qatlam. Sxema — ikkinchi.

5. Plaginlar va encapsulation

5.1 Plagin nima

Fastify'da ilova plaginlardan yig'iladi. Plagin — async (app, options) => { … } ko'rinishidagi funksiya. Ichida marshrut, hook va yordamchi obyektlar qo'shiladi. app.register(plagin) uni ulaydi.

js
async function vazifalarPlagini(app) {
  app.get("/v1/vazifalar", async () => []);
}
app.register(vazifalarPlagini);

Bu Express'dagi express.Router ga o'xshaydi, lekin bitta muhim farq bor.

5.2 Encapsulation: har plagin o'z qutisida

Decorator — Fastify nusxasiga qo'shilgan xususiyat: app.decorate("baza", pool) — keyin marshrutda this.baza. Encapsulation (inkapsulyatsiya) — plagin ichida qo'shilgan decorator va hook faqat shu plagin va uning ichidagi plaginlarga ko'rinadi. Tashqariga chiqmaydi.

Ba'zan esa narsa hammaga kerak: config, baza ulanishi. Buning uchun plagin fastify-plugin bilan o'raladi — u qutini "ochib qo'yadi".

js
import Fastify from "fastify";
import fp from "fastify-plugin";

const app = Fastify();

app.register(async (ichki) => {
  ichki.decorate("baza", { nom: "ichki baza" });
  ichki.get("/ichki", async function () {
    return { baza: this.hasDecorator("baza") };
  });
});

app.register(fp(async (umumiy) => {
  umumiy.decorate("config", { muhit: "dev" });
}));

app.get("/tashqi", async function () {
  return {
    baza: this.hasDecorator("baza"),
    config: this.hasDecorator("config"),
  };
});

console.log((await app.inject("/ichki")).body);
console.log((await app.inject("/tashqi")).body);

Konsolda (biz ishga tushirdik: fastify 5.12.7, fastify-plugin 6.0.1):

text
{"baza":true}
{"baza":false,"config":true}

baza faqat ichki plaginda bor, tashqarida — yo'q. config esa fp bilan o'ralgani uchun hammaga ko'rinadi.

flowchart TB
  R["ildiz (app)"] --> C["fp: config — hammaga"]
  R --> P1["plagin: vazifalar"]
  R --> P2["plagin: admin"]
  P2 --> H["hook: admin tekshiruvi"]
  P2 --> M["admin marshrutlari"]
  P1 --> V["vazifalar marshrutlari"]

Diagrammaga qarang: admin plagini ichidagi hook faqat admin marshrutlariga ta'sir qiladi. vazifalar marshrutlari uni "ko'rmaydi". Express'da app.use() bilan qo'yilgan middleware esa undan keyingi hamma marshrutga tushadi — tartibga e'tibor bermasangiz, admin tekshiruvi ochiq marshrutni ham yopib qo'yishi mumkin.

5.3 Plagin yuklanish tartibi

register darhol bajarilmaydi. Fastify plaginlarni navbat bilan await app.ready() (yoki listen, inject) paytida yuklaydi. Bu ishni ichida avvio kutubxonasi qiladi. Natija: plagin ichidagi async ish (bazaga ulanish) tugamaguncha server so'rov qabul qilmaydi.

Tekshirib ko'ring: Baza ulanishini app.register(async (app) => app.decorate("db", pool)) bilan qo'shdingiz, fp siz. Marshrutlar boshqa plaginda. Nima bo'ladi?

Javob

Marshrutlar this.db ni ko'rmaydi — undefined. Decorator o'z plagini qutisida qoldi. Umumiy resurslar (baza, config, logger yordamchisi) fastify-plugin bilan o'raladi.

6. Hooks: so'rov hayotining nuqtalari

6.1 Tartib

Middleware va request lifecycle darsida Express'dagi so'rov yo'lini ko'rgandik. Fastify'da bu yo'l nomli bosqichlarga bo'lingan. Har bosqichga hook ulash mumkin. Biz har hookka nomini massivga yozadigan funksiya qo'ydik va bitta POST yubordik:

js
const app = Fastify();
const tartib = [];
for (const h of ["onRequest", "preValidation",
  "preHandler", "onResponse"]) {
  app.addHook(h, async () => { tartib.push(h); });
}
for (const h of ["preParsing", "preSerialization", "onSend"]) {
  app.addHook(h, async (req, reply, payload) => {
    tartib.push(h);
    return payload;
  });
}
app.post("/h", { schema: { body: Vazifa } }, async () => {
  tartib.push("handler");
  return { ok: true };
});
await app.inject({
  method: "POST", url: "/h", payload: { matn: "x" },
});

Biz ishga tushirdik. tartib massivini chapdan o'ngga o'qing — so'rov hook'lardan shu ketma-ketlikda o'tdi:

text
onRequest > preParsing > preValidation > preHandler > handler > preSerialization > onSend > onResponse

Nima bo'ldi? Marshrut funksiyasi (handler) o'rtada: undan oldingi to'rt hook so'rovni tayyorlaydi, keyingi uchtasi javobni. Diagrammada xuddi shu yo'l:

flowchart LR
  A[onRequest] --> B[preParsing]
  B --> C[preValidation]
  C --> D[preHandler]
  D --> E[handler]
  E --> F[preSerialization]
  F --> G[onSend]
  G --> H[onResponse]
Hook Qachon Tipik ish
onRequest tana o'qilishidan oldin auth tekshiruvi, rate limit
preValidation tana o'qildi, sxemadan oldin tanani tozalash
preHandler validatsiyadan keyin huquq (rol) tekshiruvi
onSend javob yuborilishidan oldin sarlavha qo'shish
onResponse javob ketgandan keyin metrika, log

onRequest da tana hali o'qilmagan. Auth tekshiruvini aynan shu yerga qo'yish arzon: ruxsatsiz so'rovning katta tanasini o'qishga vaqt sarflanmaydi. Auth'ning o'zini 28-qismda o'rganyapmiz.

6.2 Hook yozishdagi tuzoq

preParsing, preSerialization, onSend — payload ni qaytarishi shart. Yana bir tuzoq: async hookda uchinchi argument done bo'lmasligi kerak. Biz onRequest ga uch argumentli async funksiya berdik — Fastify ro'yxatdan o'tkazishda to'xtadi:

text
FastifyError: Async function has too many arguments. Async hooks should not use the 'done' argument.

Tarjima: "async funksiyada argument ko'p. Async hooklar done argumentini ishlatmasligi kerak". Fastify argumentlar sonini (function.length) sanaydi va callback uslubi bilan async ni aralashtirishga yo'l qo'ymaydi.

7. Xatolarni Problem Details'ga aylantirish

vazifalar-api xatolarni Problem Details formatida beradi. Fastify'da buning joyi — setErrorHandler. Validatsiya xatosida err.validation massivi bor. Biz uning haqiqiy ko'rinishini chiqarib oldik (fastify 5.12.7, matn: ""):

text
{"code":"FST_ERR_VALIDATION","ctx":"body","v":[{"instancePath":"/matn","schemaPath":"#/properties/matn/minLength","keyword":"minLength","params":{"limit":1},"message":"must NOT have fewer than 1 characters"}]}

Shu obyektdan Problem Details yasaydigan toza funksiya — brauzerda ham ishlaydi:

js
function muammoYasa(xato) {
  const maydonlar = xato.validation.map((v) => ({
    maydon: v.instancePath.slice(1) || null,
    qoida: v.keyword,
    xabar: v.message,
  }));
  return {
    type: "about:blank",
    title: "Validatsiya xatosi",
    status: 400,
    detail: `${xato.validationContext}: ${maydonlar.length} ta xato`,
    errors: maydonlar,
  };
}

const xato = {
  validationContext: "body",
  validation: [{
    instancePath: "/matn",
    keyword: "minLength",
    message: "must NOT have fewer than 1 characters",
  }],
};
console.log(JSON.stringify(muammoYasa(xato), null, 2));

Konsolda:

text
{
  "type": "about:blank",
  "title": "Validatsiya xatosi",
  "status": 400,
  "detail": "body: 1 ta xato",
  "errors": [
    {
      "maydon": "matn",
      "qoida": "minLength",
      "xabar": "must NOT have fewer than 1 characters"
    }
  ]
}

Fastify'ga ulash:

js
app.setErrorHandler((err, req, reply) => {
  if (err.validation) {
    return reply.code(400)
      .type("application/problem+json")
      .send(muammoYasa(err));
  }
  req.log.error(err);
  reply.code(500).send({ title: "Ichki xato", status: 500 });
});

allErrors: false bo'lgani uchun validation massivida odatda bitta element bo'ladi — Fastify birinchi xatoda to'xtaydi. Hujjat ogohlantiradi: allErrors: true DoS hujumiga yo'l ochadi (juda katta tana minglab xato hosil qiladi).

8. TypeBox: sxema va TypeScript tipi bitta joydan

JSON Schema'ni qo'lda yozish uzun. Uning ustiga TypeScript req.body.matn turini bilmaydi. TypeBox — JSON Schema'ni Zod'ga o'xshash funksiyalar bilan yasaydigan kutubxona. Natijasi — oddiy JSON Schema obyekti, shuning uchun Fastify uni to'g'ridan tushunadi.

Biz ishga tushirdik (typebox 1.4.3):

js
import { Type } from "typebox";

const Vazifa = Type.Object(
  { matn: Type.String({ minLength: 1, maxLength: 100 }) },
  { additionalProperties: false },
);
console.log(JSON.stringify(Vazifa));
text
{"type":"object","required":["matn"],"properties":{"matn":{"type":"string","minLength":1,"maxLength":100}},"additionalProperties":false}

TypeScript'da @fastify/type-provider-typebox (6.3.0) tiplarni ulaydi — hujjat bo'yicha, kanonda sinalmagan:

ts
import Fastify from "fastify";
import { Type } from "typebox";
import type {
  TypeBoxTypeProvider,
} from "@fastify/type-provider-typebox";

const app = Fastify().withTypeProvider<TypeBoxTypeProvider>();

app.post("/v1/vazifalar", {
  schema: { body: Type.Object({ matn: Type.String() }) },
}, async (req) => {
  return { uzunlik: req.body.matn.length }; // matn: string
});

E'tibor bering: TypeBoxTypeProvider — faqat tip. Biz uni JS faylda oddiy import { TypeBoxTypeProvider } bilan olmoqchi bo'ldik va Node xato berdi: SyntaxError: The requested module '@fastify/type-provider-typebox' does not provide an export named 'TypeBoxTypeProvider'. TS'da import type yozing.

Zod'ni yaxshi ko'rsangiz — fastify-type-provider-zod jamoa paketi bor (sinalmagan). Lekin u holda Fastify'ning sxema kompilyatsiyasi tezligidan qisman voz kechasiz.

9. Ekotizim: @fastify/*

Express'da cors, helmet kabi alohida paketlar bor edi. Fastify'da rasmiy plaginlar @fastify/ nomi ostida:

Paket Vazifasi Express'dagi o'xshashi
@fastify/cors CORS cors
@fastify/helmet xavfsizlik sarlavhalari helmet
@fastify/rate-limit so'rovlarni cheklash express-rate-limit
@fastify/multipart fayl yuklash Multer
@fastify/swagger sxemalardan OpenAPI qo'lda
@fastify/autoload papkadagi plaginlarni yuklash —

@fastify/swagger — Fastify'ning kuchli tomoni: marshrut sxemalari allaqachon yozilgan, hujjat ulardan avtomatik yig'iladi (OpenAPI 3.1 darsidagi spetsifikatsiya). Bu jadvaldagi plaginlar kanonda sinalmagan, nomlari va vazifalari — github.com/fastify ro'yxati bo'yicha.

Express middleware'ini to'g'ridan Fastify'ga ulab bo'lmaydi. @fastify/express yoki @fastify/middie orqali qisman mumkin, lekin ko'chishda odatda Fastify plaginiga almashtiriladi.

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Ortiqcha maydon xatosiz yo'qoldi sukut removeAdditional: true sukutni bilib qabul qiling yoki false qiling
"42" satr bo'lib saqlandi sukut coerceTypes tana uchun qat'iy rejim
Javobda maydon yo'q javob sxemasida yozilmagan sxemaga qo'shing
this.db — undefined decorator boshqa plagin qutisida fastify-plugin bilan o'rang
Cannot call "addHook" inject/listen dan keyin hook qo'shildi hamma narsani start'dan oldin
Async hooks should not use the 'done' argument async + done aralash bittasini tanlang
Marshrut ichida res.json yo'q Express odati qiymatni return qiling

11. Mashqlar

Mashqlar kurs/mashqlar/29/63-fastify/ da. 2- va 3-mashq uchun alohida papkada npm i fastify@5.12.7 fastify-plugin@6.0.1.

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

  • Fastify tanani tekshirish uchun ichida kutubxonasini ishlatadi.
  • Sukutda additionalProperties: false dagi ortiqcha maydon (xato bermaydi).
  • Plagin decorator'ini hammaga ko'rinadigan qilish uchun uni bilan o'raymiz.
  • Hooklardan eng birinchisi —
  • Portsiz test usuli — app.()
Yechim
  • Ajv — JSON Schema tekshiruvchisi.
  • o'chiriladi — removeAdditional: true sukutda.
  • fastify-plugin — encapsulation'ni o'chiradi.
  • onRequest — tana hali o'qilmagan.
  • inject — light-my-request orqali.

2-mashq (o'rta): params va javob sxemasi

GET /v1/narxlar/:id marshrutini yozing. params sxemasi: id — integer, minimum: 1. Javob sxemasi: nom (string) va narx (integer). Marshrut ichida narxlar massividan topib, { nom, narx, tannarx } qaytarsin (tannarx — ichki maydon). Topilmasa — 404 { xato: "topilmadi" } (404 uchun ham javob sxemasi kerak). inject bilan /v1/narxlar/2, /v1/narxlar/9, /v1/narxlar/abc ni sinang. Ishora: params satr bo'lib keladi, sukutdagi coerceTypes uni songa aylantiradi.

Yechim
js
import Fastify from "fastify";

const narxlar = [
  { id: 1, nom: "Osh", narx: 35000, tannarx: 21000 },
  { id: 2, nom: "Lag'mon", narx: 28000, tannarx: 15000 },
];

const app = Fastify();
app.get("/v1/narxlar/:id", {
  schema: {
    params: {
      type: "object",
      properties: { id: { type: "integer", minimum: 1 } },
    },
    response: {
      200: {
        type: "object",
        properties: {
          nom: { type: "string" },
          narx: { type: "integer" },
        },
      },
      404: {
        type: "object",
        properties: { xato: { type: "string" } },
      },
    },
  },
}, async (req, reply) => {
  const t = narxlar.find((n) => n.id === req.params.id);
  if (!t) return reply.code(404).send({ xato: "topilmadi" });
  return t;
});

for (const id of ["2", "9", "abc"]) {
  const r = await app.inject(`/v1/narxlar/${id}`);
  console.log(r.statusCode, r.body);
}

Konsolda (biz ishga tushirdik: Node 24.21.0, fastify 5.12.7):

text
200 {"nom":"Lag'mon","narx":28000}
404 {"xato":"topilmadi"}
400 {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"params/id must be integer"}

"2" songa aylandi va === bilan topildi. tannarx javob sxemasida yo'q — chiqmadi. "abc" songa aylana olmadi — 400.

3-mashq (qiyin): kalit tekshiruvi faqat admin plaginida

Ilova yozing: ochiq marshrut GET /v1/menyu ({ taomlar: 3 }) va admin plagini (prefix: "/admin"). Plaginda onRequest hook x-api-kalit sarlavhasini tekshirsin: "sinov-kaliti" bo'lmasa — 401 { xato: "kalit kerak" }. Plagin ichida GET /hisobot — { tushum: 1250000 }. inject bilan: menyu (kalitsiz), hisobot (kalitsiz), hisobot (kalit bilan). Ishora: hook ichida reply.code(401).send(...) dan keyin return reply; register ning ikkinchi argumenti — { prefix }.

Yechim
js
import Fastify from "fastify";

const app = Fastify();

app.get("/v1/menyu", async () => ({ taomlar: 3 }));

app.register(async (admin) => {
  admin.addHook("onRequest", async (req, reply) => {
    if (req.headers["x-api-kalit"] !== "sinov-kaliti") {
      reply.code(401).send({ xato: "kalit kerak" });
      return reply;
    }
  });
  admin.get("/hisobot", async () => ({ tushum: 1250000 }));
}, { prefix: "/admin" });

const sorovlar = [
  ["/v1/menyu", {}],
  ["/admin/hisobot", {}],
  ["/admin/hisobot", { "x-api-kalit": "sinov-kaliti" }],
];
for (const [url, headers] of sorovlar) {
  const r = await app.inject({ url, headers });
  console.log(r.statusCode, url, r.body);
}

Konsolda (biz ishga tushirdik: Node 24.21.0, fastify 5.12.7):

text
200 /v1/menyu {"taomlar":3}
401 /admin/hisobot {"xato":"kalit kerak"}
200 /admin/hisobot {"tushum":1250000}

Hook plagin qutisida — menyu unga tushmadi. Bu darsdagi kalit — faqat mashq uchun. Haqiqiy API kalitlar va ularni xavfsiz solishtirish — API kalitlar va servislararo autentifikatsiya darsida.

12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

Bu darsda vazifalar-api o'zgarmaydi — u Express 5 da qoladi. Kanon Fastify'ni faqat tajribada sinadi: bir xil API (GET JSON, POST validatsiya) sakkiz serverda, autocannon bilan. Natijani grafikda ko'rdingiz.

Nega ko'chmaymiz? Uch sabab:

  1. Loyihamizda vaqtning asosiy qismi Postgres so'rovlarida — freymvork farqi kichik (25-qismda Express bazaga ulangan holda 9 724 so'rov/s).
  2. Butun middleware zanjiri (Zod, Pino, Problem Details, testlar) qayta yoziladi — risk katta, foyda kichik.
  3. 30-qismda NestJS'ga o'tamiz, u Fastify'ni "adapter" sifatida ulay oladi (Fastify adapter bilan NestJS). Fastify bilimi o'sha yerda qayta kerak bo'ladi.

O'zingiz sinab ko'ring: alohida papkada npm i fastify@5.12.7 fastify-plugin@6.0.1, 3-mashqni yurgizing. Keyin admin plaginini fp() bilan o'rab, menyu so'rovi ham 401 olishini ko'ring — encapsulation qanday ochilishini o'z ko'zingiz bilan.

13. Real ishda

  • Yuqori yuklamali API'lar — Fastify Node'da Express'dan keyingi eng ko'p ishlatiladigan backend freymvorklaridan biri. Yangi loyihalar ko'pincha shu bilan boshlanadi.
  • NestJS ichida — FastifyAdapter (Fastify adapter bilan NestJS).
  • Sxema-birinchi yondashuv — marshrut sxemasi bir vaqtda validatsiya, serializatsiya va OpenAPI hujjati.
  • Intervyuda: "Fastify nega Express'dan tez?", "Plagin encapsulation nima?", "Fastify'da hooks tartibi?", "Javob sxemasi nima beradi?"

Xulosa

  • Fastify — JSON Schema'ga asoslangan Node freymvorki: tekshiruv (Ajv) va serializatsiya (fast-json-stringify) ichida.
  • Sukut Ajv sozlamasi ortiqcha maydonni jim o'chiradi va turni o'zgartiradi — bilib tanlang.
  • Javob sxemasi sxemada yo'q maydonni (masalan, parol hash'i) chiqarmaydi.
  • Plagin — quti: decorator va hook tashqariga chiqmaydi; umumiy narsa — fastify-plugin.
  • Hooks tartibi: onRequest → preParsing → preValidation → preHandler → handler → preSerialization → onSend → onResponse.
  • Kanonda Fastify GET'da 20 514, Express 10 698 so'rov/s; baza bilan farq kichrayadi.

Keyingi dars: Hono — Web standartlari (Request, Response) ustiga qurilgan freymvork: bir xil kod Node, Bun, Deno va Cloudflare Workers'da ishlaydi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

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