Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. Birinchi server va inject()
- 2.1 O'rnatish va marshrut
- 2.2 Portsiz test: inject()
- 3. JSON Schema bilan validatsiya
- 3.1 Tana sxemasi
- 3.2 Ikki tuzoq: sukut Ajv sozlamasi
- 3.3 Qat'iy rejim
- 4. Javob sxemasi: serializatsiya
- 4.1 Maxfiy maydon kesiladi
- 4.2 Lekin bu yagona himoya emas
- 5. Plaginlar va encapsulation
- 5.1 Plagin nima
- 5.2 Encapsulation: har plagin o'z qutisida
- 5.3 Plagin yuklanish tartibi
- 6. Hooks: so'rov hayotining nuqtalari
- 6.1 Tartib
- 6.2 Hook yozishdagi tuzoq
- 7. Xatolarni Problem Details'ga aylantirish
- 8. TypeBox: sxema va TypeScript tipi bitta joydan
- 9. Ekotizim: @fastify/*
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): params va javob sxemasi
- 3-mashq (qiyin): kalit tekshiruvi faqat admin plaginida
- 12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 13. Real ishda
- Xulosa
- Manbalar
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-pluginva 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:
- Tezlik. Fastify sxemani oldindan "kompilyatsiya" qiladi: tekshiruvchi va JSON yozuvchi funksiya bir marta yasaladi, keyin har so'rovda tayyor ishlatiladi.
- 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:
- 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
npm i fastify@5.12.7Kanon versiyasi — Fastify 5.12.7 (Node 24.21.0). Fastify 5 Node 20 va undan yangisini talab qiladi (rasmiy "Migration Guide V5").
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:
asyncfunksiya 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,pidvareqIdbor — har safar boshqacha, shuning uchun bu yerda chiqishini bermaymiz.- Marshrut ichidagi
throwavtomatik 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.
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 keyinaddHookyoki yangi marshrut qo'shib bo'lmaydi — biz sinab ko'rdik:FastifyError: Fastify instance is already listening. Cannot call "addHook"!. Testda har holat uchun yangiFastify()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):
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:
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:
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:
201 {"id":1,"matn":"Non"}
201 {"id":1,"matn":"42"}Ikkalasi ham o'tdi:
additionalProperties: falsebo'lsa hamadminuchun xato yo'q. Fastify uni jim o'chirib tashladi.42soni"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'rovidamatnbo'sh edi — 400 aynan shuminLengthuchun 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:
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):
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=2marshrutidaquerystringsxemasisahifa: { type: "integer" }.coerceTypes: falseqilsangiz 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:
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.
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".
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):
{"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,fpsiz. 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:
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:
onRequest > preParsing > preValidation > preHandler > handler > preSerialization > onSend > onResponseNima 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:
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: ""):
{"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:
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:
{
"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:
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):
import { Type } from "typebox";
const Vazifa = Type.Object(
{ matn: Type.String({ minLength: 1, maxLength: 100 }) },
{ additionalProperties: false },
);
console.log(JSON.stringify(Vazifa));{"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:
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: falsedagi 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: truesukutda. - fastify-plugin — encapsulation'ni o'chiradi.
- onRequest — tana hali o'qilmagan.
- inject —
light-my-requestorqali.
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
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):
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
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):
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:
- Loyihamizda vaqtning asosiy qismi Postgres so'rovlarida — freymvork farqi kichik (25-qismda Express bazaga ulangan holda 9 724 so'rov/s).
- Butun middleware zanjiri (Zod, Pino, Problem Details, testlar) qayta yoziladi — risk katta, foyda kichik.
- 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
- Fastify hujjati (Getting Started, Plugins, Encapsulation, Hooks, Decorators): https://fastify.dev/docs/latest/
- Validation and Serialization (sukut Ajv sozlamasi, javob sxemasi): https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/
- Migration Guide V5: https://fastify.dev/docs/latest/Guides/Migration-Guide-V5/
- fastify-plugin: https://github.com/fastify/fastify-plugin
- TypeBox type provider: https://github.com/fastify/fastify-type-provider-typebox
- Kanon tajribasi (IlmHamroh, 2026-10-27): fastify 5.12.7, express 5.3.0, autocannon 8.0.0, Node 24.21.0, Windows 11. Sukut Ajv xatti-harakati, hooks tartibi, encapsulation va mashqlar — biz ishga tushirdik (2026-10-28, o'sha versiyalar, fastify-plugin 6.0.1, typebox 1.4.3).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!