Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. zod-to-openapi bilan hujjat yasash
- 2.1 Sxemalarni ro'yxatga olish
- 2.2 Marshrutni tasvirlash
- 2.3 z.toJSONSchema() bilan farq
- 3. Hujjatni berish va ko'rsatish
- 4. Hujjatdan TypeScript tiplari
- 4.1 openapi-typescript
- 4.2 Klient generatorlari
- 5. Breaking change
- 5.1 Nima buzadi, nima buzmaydi
- 5.2 O'zimiz yozamiz
- 6. Hujjat sifatini tekshirish: Spectral
- 7. Kontrakt test: hujjat va javob mosmi?
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): kichik tip generatori
- 3-mashq (qiyin): enum o'zgarishlari
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
Code-first OpenAPI: Zod'dan hujjat, TypeScript klient va breaking change tekshiruvi
Qisqacha: Code-first yondashuvda OpenAPI hujjati qo'lda yozilmaydi — u validatsiya uchun allaqachon bor Zod sxemalaridan avtomatik yasaladi (
@asteasolutions/zod-to-openapi). Hujjat/openapi.jsonda beriladi va Swagger UI, Scalar yoki Redoc uni chiroyli sahifaga aylantiradi. Frontend tomondaopenapi-typescripthujjatdan TypeScript tiplarini chiqaradi, orval yoki hey-api esa tayyor klient funksiyalarini. CI'da Spectral hujjat sifatini, oasdiff kabi vositalar esa mijozlarni buzadigan o'zgarishni ushlaydi.
Bu darsda
- Zod sxemalari va
registerPathdan OpenAPI 3.1 hujjatini yasaysiz va kanon natijasini o'qiysiz. zod-to-openapiva Zod'ning o'zz.toJSONSchema()natijasi farqini bilasiz.openapi-typescriptchiqargan tiplarni frontend'da ishlatasiz va klient generatorlarini solishtirasiz.- Breaking change nima ekanini va uni avtomatik qanday aniqlashni tushuntirasiz.
Oldin bilishingiz kerak: OpenAPI 3.1 spetsifikatsiyasi, Zod 4 serverda, API versiyalash va deprecation.
1. Nega bu kerak?
Oldingi darsda openapi.yaml ni qo'lda yozdik. Endi vazifalar-api ga qaraylik: matn uchun "1 dan 100 gacha belgi" qoidasi qayerda yashaydi? Zod sxemasida (Zod 4 serverda). Qo'lda yozilgan YAML'da ham minLength: 1, maxLength: 100 bo'ladi. Ikki joy — ikki haqiqat. Kimdir 100 ni 200 qiladi, ikkinchisini unutadi.
Code-first yondashuv: haqiqat bitta — kod. Zod sxemasi so'rovni tekshiradi va hujjatga aylanadi. Hujjat build yoki ishga tushish paytida yasaladi, u kod bilan hech qachon ajralmaydi.
Shu darsda zanjirni to'liq ko'ramiz:
flowchart LR
Z["Zod sxemalari"] --> G["zod-to-openapi"]
G --> J["openapi.json"]
J --> U["Swagger UI / Scalar"]
J --> L["Spectral: lint"]
J --> D["oasdiff: breaking change"]
J --> T["openapi-typescript"]
T --> F["Frontend tiplari"]Kanon tajribasi (2026-10-27): zod-to-openapi 9.1.0, zod 4.6.7, openapi-typescript 7.13.0, Node 24.21.0. Swagger UI, Scalar, Redoc, orval, hey-api, Spectral va oasdiff — sinalmagan, ular hujjat bo'yicha (2026-10). To'liq hujjat va tiplar chiqishini biz kanon kodi bilan qayta ishga tushirib oldik.
2. zod-to-openapi bilan hujjat yasash
2.1 Sxemalarni ro'yxatga olish
@asteasolutions/zod-to-openapi Zod'ga .openapi() metodini qo'shadi va registry (ro'yxat) beradi: unga sxemalar, xavfsizlik sxemalari va marshrutlar yoziladi. Kanon kodi (telefon uchun bo'ldik):
import { z } from "zod";
import {
OpenAPIRegistry,
OpenApiGeneratorV31,
extendZodWithOpenApi,
} from "@asteasolutions/zod-to-openapi";
extendZodWithOpenApi(z);
const reg = new OpenAPIRegistry();
const Vazifa = reg.register(
"Vazifa",
z.object({
id: z.number().int().openapi({ example: 42 }),
matn: z.string().min(1).max(100),
bajarildi: z.boolean(),
muddat: z.iso.datetime().nullable().optional(),
}).openapi({ description: "Bitta vazifa" }),
);
const Problem = reg.register("Problem", z.object({
type: z.string(),
title: z.string(),
status: z.number().int(),
detail: z.string().optional(),
}));
reg.registerComponent("securitySchemes", "cookieAuth", {
type: "apiKey", in: "cookie", name: "__Host-sessiya",
});reg.register("Vazifa", sxema) — sxemani components/schemas/Vazifa ga yozadi va o'sha sxemani qaytaradi. Qaytgan Vazifa — oddiy Zod sxemasi: u bilan Vazifa.parse(body) qilish ham mumkin. Bitta obyekt — validatsiya va hujjat.
2.2 Marshrutni tasvirlash
reg.registerPath({
method: "get",
path: "/v1/vazifalar/{id}",
summary: "Vazifani olish",
security: [{ cookieAuth: [] }],
request: {
params: z.object({ id: z.coerce.number().int().positive() }),
},
responses: {
200: {
description: "OK",
content: { "application/json": { schema: Vazifa } },
},
404: {
description: "Topilmadi",
content: { "application/problem+json": { schema: Problem } },
},
},
});
const generator = new OpenApiGeneratorV31(reg.definitions);
const spec = generator.generateDocument({
openapi: "3.1.0",
info: { title: "vazifalar-api", version: "3.0.0" },
});
console.log(JSON.stringify(spec).length, "bayt");Kanon natijasi:
hujjat: 1141 bayt; Vazifa sxemasi: {"type":"object","properties":{"id":{"type":"integer","example":42},"matn":{"type":"string","minLength":1,"maxLength":100},"bajarildi":{"type":"boolean"},"muddat":{"type":["string","null"],"format":"date-time"}},"required":["id","matn","bajarildi"],"description":"Bitta vazifa"}Zod'dagi har qoida JSON Schema'ga o'tdi:
| Zod | OpenAPI 3.1 |
|---|---|
z.number().int() |
"type": "integer" |
.min(1).max(100) |
minLength, maxLength |
.nullable() |
"type": ["string", "null"] |
.optional() |
required ro'yxatida yo'q |
z.iso.datetime() |
"format": "date-time" |
.positive() (parametr) |
"exclusiveMinimum": 0 |
Oxirgi qatorni biz to'liq hujjatdan oldik — id parametri shunday chiqdi:
{
"schema": { "type": "integer", "exclusiveMinimum": 0 },
"required": true,
"name": "id",
"in": "path"
}registerPath marshrutning o'zini yaratmaydi — u faqat tasvir. Express marshruti alohida yoziladi. Demak bitta xavf qoladi: marshrut kodda bor, registerPath da yo'q (yoki aksincha). Kichik loyihada buni kontrakt test ushlaydi (pastda). Fastify va NestJS (30-qism) marshrut va sxemani bitta joyda e'lon qiladi — bu muammo u yerda kamroq.
2.3 z.toJSONSchema() bilan farq
Zod 4 da JSON Schema'ga o'tkazish o'zida bor — z.toJSONSchema() (Zod 4 serverda). Unda nega alohida kutubxona? Biz o'sha matn va muddat maydonlarini ikkala yo'l bilan o'tkazdik (zod 4.6.7). z.toJSONSchema natijasi (uzun regex'ni … bilan qisqartirdik):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"matn": { "type": "string", "minLength": 1, "maxLength": 100 },
"muddat": {
"anyOf": [
{ "type": "string", "format": "date-time", "pattern": "…" },
{ "type": "null" }
]
}
},
"required": ["matn"],
"additionalProperties": false
}Uch farq:
nullable—anyOfbilan (zod-to-openapi:typemassivi). Ikkalasi ham to'g'ri JSON Schema, lekin ba'zi klient generatorlarianyOfdan noqulay tip yasaydi.additionalProperties: false— Zodz.objectsukut bo'yicha ortiqcha kalitlarni tashlab yuboradi,toJSONSchemaesa buni "ortiqcha kalit taqiqlangan" deb yozdi. zod-to-openapi bu kalitni qo'ymadi.pattern— sana uchun uzun regex qo'shildi; zod-to-openapi faqatformatyozdi.
Asosiy farq esa bu jadvalda emas: toJSONSchema faqat sxema beradi. paths, parametrlar, javoblar, xavfsizlik va $ref bilan components — zod-to-openapi ishi. Bitta sxemani hujjatga qo'shish uchun toJSONSchema yetadi, butun API uchun — registry.
3. Hujjatni berish va ko'rsatish
Hujjat odatda ikki marshrutda: mashina uchun GET /openapi.json, odam uchun GET /docs. Express'da birinchisi bir qator:
app.get("/openapi.json", (_req, res) => res.json(spec));/docs uchun uchta mashhur vosita (hujjat bo'yicha, sinalmagan):
| Vosita | Xususiyati |
|---|---|
| Swagger UI | eng keng tarqalgan; "Try it out" — brauzerdan so'rov yuborish |
| Scalar | zamonaviy ko'rinish, ichida so'rov yuborish va kod namunalari |
| Redoc | uch ustunli o'qish uchun hujjat; so'rov yuborish yo'q |
Uchalasi ham openapi.json ni o'qiydigan statik sahifa: CDN'dan skript va bitta HTML. Production'da /docs ni ochiq qoldirish — qaror: ichki API bo'lsa, faqat ichki tarmoqda yoki autentifikatsiya bilan. "Try it out" haqiqiy so'rov yuboradi — prod ma'lumotiga tegishi mumkin.
Diqqat:
/docssahifasi tashqi CDN'dan skript yuklasa, Helmet va xavfsizlik header'lari darsidagi CSP uni bloklaydi. CSP'ni butun API uchun bo'shatmang — faqat/docsmarshruti uchun alohida siyosat bering yoki skriptni o'zingizda saqlang.
4. Hujjatdan TypeScript tiplari
4.1 openapi-typescript
Frontend tomonda hujjatdan tip chiqaramiz. openapi-typescript — hujjatni o'qib, .d.ts fayl yozadi; runtime kodi yo'q. Kanon chiqishidan parcha:
openapi-typescript → TS (parcha): Vazifa: { matn: string; muddat?: string | null;To'liq natijada (biz ishga tushirdik, openapi-typescript 7.13.0) components qismi:
export interface components {
schemas: {
/** @description Bitta vazifa */
Vazifa: {
/** @example 42 */
id: number;
matn: string;
bajarildi: boolean;
/** Format: date-time */
muddat?: string | null;
};
// Problem — xuddi shunday
};
}muddat?: string | null — ? — optional, | null — nullable. Ikki holat frontend'da ham ajralib turibdi. integer TS'da number bo'ldi — TS'da butun son tipi yo'q. date-time — string: tip Date emas, chunki JSON'da sana satr bo'lib keladi.
paths qismida esa har amalning parametrlari va javoblari:
export interface paths {
"/v1/vazifalar/{id}": {
get: {
parameters: { path: { id: number } };
responses: {
200: {
content: {
"application/json": components["schemas"]["Vazifa"];
};
};
// 404 — Problem
};
};
};
}(Bu ikki blokni o'qish uchun qisqartirdik: generator har amalga query?: never, header?: never kabi bo'sh qismlarni ham yozadi.)
Frontend'da ishlatish:
import type { components } from "./api-tiplari";
type Vazifa = components["schemas"]["Vazifa"];
function muddatMatni(v: Vazifa): string {
return v.muddat ?? "muddatsiz";
}19-qismda Vazifa tipini qo'lda yozgandik. Endi u backend sxemasidan keladi. Backend bajarildi ni o'chirsa, frontend build'i tip xatosi bilan yiqiladi — ishlab chiqarishda emas.
4.2 Klient generatorlari
Tiplar bor, fetch chaqiruvlari esa hali qo'lda. Keyingi qadam — tayyor klient. Uch yo'l (hujjat bo'yicha, sinalmagan):
| Vosita | Nima yasaydi | Qachon |
|---|---|---|
openapi-fetch |
tiplar ustida yupqa fetch o'rami, kod generatsiyasiz |
eng kichik, openapi-typescript bilan juft |
| orval | funksiyalar, TanStack Query hook'lari, MSW mock'lari | React + TanStack Query loyihasi |
hey-api (@hey-api/openapi-ts) |
SDK funksiyalar, Zod sxemalar, plaginlar | to'liq SDK kerak bo'lsa |
openapi-fetch g'oyasi: client.GET("/v1/vazifalar/{id}", { params: { path: { id: 7 } } }) — yo'l satri ham, parametr ham, javob ham tipli. Yo'lni xato yozsangiz — tip xatosi. orval esa useVazifaOlish(7) kabi hook'ni o'zi yozadi (TanStack Query I bilan). Tanlov — loyihada qaysi data-fetching usuli ishlatilishiga bog'liq.
Generatsiya qilingan kod qo'lda tahrirlanmaydi — keyingi generatsiya uni o'chiradi. Odatda npm run api:tiplar skripti va CI'da "generatsiya qilingan fayl yangimi?" tekshiruvi.
5. Breaking change
5.1 Nima buzadi, nima buzmaydi
Breaking change — mavjud mijozni buzadigan o'zgarish (API versiyalash va deprecation). Qoida yo'nalishga bog'liq: server javobda va'da qiladi, so'rovda talab qiladi.
| O'zgarish | Javobda | So'rovda |
|---|---|---|
| Maydon olib tashlandi | buzadi (mijoz o'qiydi) | buzmaydi |
| Yangi ixtiyoriy maydon | buzmaydi | buzmaydi |
| Yangi majburiy maydon | buzmaydi | buzadi (eski mijoz yubormaydi) |
| Tur o'zgardi | buzadi | buzadi |
null ga ruxsat berildi |
buzadi (mijoz null kutmaydi) |
buzmaydi |
Tekshirib ko'ring: Javobdagi
idturiniintegerdanstringga o'zgartirdik (UUID'ga o'tish). Bu buzadimi?
Javob
Ha, ikkala yo'nalishda ham. Frontend id ni son deb solishtiradi (v.id === 7), URL'ga qo'yadi, saralaydi — hammasi o'zgaradi. Bunday o'tish odatda yangi maydon (uuid) qo'shish, mijozlarni unga ko'chirish va eski maydonni deprecation bilan keyinroq olib tashlash orqali qilinadi — Zero-downtime: expand-contract g'oyasining API'dagi ko'rinishi.
Kanon oddiy tekshiruvni yozgan: hujjat nusxasida bajarildi ni o'chirib, muddat ni majburiy qilgan:
oddiy breaking-change tekshiruvi: javobdan maydon olib tashlandi: bajarildi; yangi majburiy maydon: muddat (so'rovda bo'lsa — breaking) | (oasdiff — Go binar, sinalmagan)5.2 O'zimiz yozamiz
Kanon tekshiruvini sxemaning qayerda ishlatilishini hisobga oladigan qilib yozamiz:
function farqlar(eski, yangi, joy) {
const x = [];
for (const [k, s] of Object.entries(eski.properties)) {
const ys = yangi.properties[k];
if (!ys) {
if (joy === "javob") x.push(`BUZADI: ${k} olib tashlandi`);
continue;
}
const et = [s.type].flat().join("|");
const yt = [ys.type].flat().join("|");
if (et !== yt) {
const nullQoshildi = yt === `${et}|null`;
if (joy === "javob" || !nullQoshildi) {
x.push(`BUZADI: ${k} turi ${et} → ${yt}`);
}
}
}
for (const k of yangi.required ?? []) {
if (!(eski.required ?? []).includes(k) && joy === "sorov") {
x.push(`BUZADI: ${k} endi majburiy`);
}
}
return x.length ? x : ["xavfsiz"];
}
const v1 = {
properties: {
matn: { type: "string" },
bajarildi: { type: "boolean" },
muddat: { type: "string" },
},
required: ["matn"],
};
const v2 = {
properties: {
matn: { type: "string" },
muddat: { type: ["string", "null"] },
teg: { type: "string" },
},
required: ["matn", "teg"],
};
console.log("javob:", farqlar(v1, v2, "javob"));
console.log("so'rov:", farqlar(v1, v2, "sorov"));Konsolda:
javob: [
'BUZADI: bajarildi olib tashlandi',
'BUZADI: muddat turi string → string|null'
]
so'rov: [ 'BUZADI: teg endi majburiy' ]Bir xil o'zgarish ikki joyda ikki xil baho oldi. bajarildi olib tashlandi — javobda buzadi, so'rovda emas. muddat ga null qo'shildi — javobda buzadi (frontend v.muddat.slice() qilsa yiqiladi), so'rovda emas. teg majburiy bo'ldi — so'rovda buzadi.
Haqiqiy vositalar (oasdiff — Go'da yozilgan, Node paketi emas; openapi-diff va boshqalar) yuzlab qoidani tekshiradi: yo'l o'chirilishi, status kod o'zgarishi, enum qiymatining olib tashlanishi, parametr joyining o'zgarishi. Ularning vazifasi bitta: pull request'da "bu o'zgarish mijozlarni buzadi" deb CI'ni to'xtatish. Buzish kerak bo'lsa — yangi versiya (/v2/) va eski versiyani bosqichma-bosqich yopish.
6. Hujjat sifatini tekshirish: Spectral
Hujjat to'g'ri formatda bo'lishi bilan yaxshi bo'lishi boshqa narsa. Spectral (Stoplight) — OpenAPI uchun linter, ESLint kabi (hujjat bo'yicha, sinalmagan). Uning sukut qoidalari to'plami spectral:oas quyidagilarni ushlaydi: operationId yo'qligi yoki takrorlanishi, description siz amallar, ishlatilmagan components, servers yo'qligi. O'z qoidangizni ham yozasiz: "har amalda 4xx javob bo'lsin", "yo'llar kebab-case'da".
CI'dagi tartib odatda shunday:
openapi.jsonni generatsiya qilish (testlar bilan birga).- Spectral — xato bo'lsa CI to'xtaydi.
maindagi hujjat bilan diff — breaking bo'lsa, PR'da ogohlantirish yoki to'xtatish.- Frontend tiplarini qayta generatsiya qilish va build.
CI'ni 34-qismda quramiz. Hozircha bu bosqichlarni lokal npm run check skriptiga qo'shish ham yetadi.
7. Kontrakt test: hujjat va javob mosmi?
Code-first'da ham hujjat va haqiqiy javob ajralishi mumkin: registerPath da 200 javob Vazifa, handler esa { vazifa: {...} } qaytaradi. Buni test ushlaydi. G'oya: integratsiya testida javobni hujjatdagi sxema bilan tekshirish. Code-first'da bu ayniqsa oson — sxema Zod'da bor:
test("GET /v1/vazifalar/:id javobi shartnomaga mos", async () => {
const r = await request(app).get("/v1/vazifalar/1").set(kuki);
assert.equal(r.status, 200);
assert.ok(Vazifa.safeParse(r.body).success, "javob Vazifa emas");
});Supertest bilan HTTP integration test darsidagi naqsh — faqat oxirgi qator yangi. Spec-first loyihada xuddi shu ishni Ajv hujjatdagi JSON Schema bilan qiladi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
.openapi is not a function |
extendZodWithOpenApi(z) chaqirilmagan |
ilova boshida bir marta |
Sxema hujjatda inline, $ref yo'q |
reg.register emas, oddiy Zod sxemasi |
nomli sxemalarni ro'yxatga oling |
| Marshrut bor, hujjatda yo'q | registerPath unutilgan |
kontrakt test yoki marshrut ro'yxatini solishtirish |
| Frontend tiplari eski | generatsiya qo'lda, unutilgan | npm run skripti va CI tekshiruvi |
| Generatsiya qilingan faylga tuzatish yo'qoldi | qo'lda tahrirlangan | sxemani manbada tuzating |
/docs bo'sh oq sahifa |
CSP tashqi skriptni blokladi | /docs uchun alohida siyosat |
9. Mashqlar
Mashqlar kurs/mashqlar/29/58-code-first/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- Zod'ga
.openapi()qo'shadigan chaqiruv — - Hujjatdan faqat TS tiplarini chiqaradigan vosita — openapi-
.optional()+.nullable()dan chiqqan TS tipi —muddat?: string |- Javobdan maydonni olib tashlash — change
- OpenAPI uchun linter —
Yechim
- extendZodWithOpenApi —
extendZodWithOpenApi(z). - typescript —
openapi-typescript, runtime kodi yo'q. - null —
?optional,| nullnullable. - breaking — mavjud mijoz o'qiydigan maydon yo'qoldi.
- Spectral —
spectral:oasqoidalari.
2-mashq (o'rta): kichik tip generatori
tipYasa(nom, sxema) funksiyasini yozing — openapi-typescript ning eng kichik varianti. U type Nom = { ... }; satrini qaytarsin: required da bo'lmagan maydonga ?; integer/number → number, string → string, boolean → boolean, "null" → null, turlar massivi → | bilan; enum bo'lsa — qiymatlar "a" | "b"; type: "array" — items turi va [].
Yechim
const TS = {
integer: "number", number: "number", string: "string",
boolean: "boolean", null: "null",
};
function turTS(s) {
if (s.enum) return s.enum.map((q) => JSON.stringify(q)).join(" | ");
if (s.type === "array") return `${turTS(s.items)}[]`;
return [s.type].flat().map((t) => TS[t] ?? "unknown").join(" | ");
}
function tipYasa(nom, sxema) {
const majburiy = new Set(sxema.required ?? []);
const qatorlar = Object.entries(sxema.properties).map(([k, s]) => {
const belgi = majburiy.has(k) ? "" : "?";
return ` ${k}${belgi}: ${turTS(s)};`;
});
return `type ${nom} = {\n${qatorlar.join("\n")}\n};`;
}
console.log(tipYasa("Vazifa", {
type: "object",
properties: {
id: { type: "integer" },
matn: { type: "string" },
ustuvorlik: { enum: ["past", "orta", "yuqori"] },
teglar: { type: "array", items: { type: "string" } },
muddat: { type: ["string", "null"] },
},
required: ["id", "matn", "ustuvorlik"],
}));Konsolda:
type Vazifa = {
id: number;
matn: string;
ustuvorlik: "past" | "orta" | "yuqori";
teglar?: string[];
muddat?: string | null;
};teglar — string[], ustuvorlik — literal tiplar birlashmasi: frontend "juda" deb yozsa, tip xatosi. Haqiqiy generator yana $ref (components["schemas"]["X"]), oneOf/allOf, additionalProperties (Record<string, …>) va izohlarni (/** @description */) biladi.
3-mashq (qiyin): enum o'zgarishlari
Darsdagi farqlar ga enum qoidalarini qo'shing. Enum'dan qiymat olib tashlansa — so'rovda buzadi (eski mijoz uni yuborishi mumkin), javobda buzmaydi. Enum'ga qiymat qo'shilsa — javobda buzadi (eski mijoz switch da yangi qiymatni bilmaydi), so'rovda buzmaydi. Faqat enum qismini yozing: enumFarq(eski, yangi, joy) — eski va yangi — maydon sxemalari.
Yechim
function enumFarq(eski, yangi, joy) {
const e = new Set(eski.enum ?? []);
const y = new Set(yangi.enum ?? []);
const ketdi = [...e].filter((q) => !y.has(q));
const keldi = [...y].filter((q) => !e.has(q));
const x = [];
if (ketdi.length && joy === "sorov") {
x.push(`BUZADI: olib tashlandi ${ketdi.join(", ")}`);
}
if (keldi.length && joy === "javob") {
x.push(`BUZADI: qo'shildi ${keldi.join(", ")}`);
}
return x.length ? x : ["xavfsiz"];
}
const eski = { enum: ["past", "orta", "yuqori"] };
const yangi = { enum: ["past", "orta", "yuqori", "shoshilinch"] };
const kichik = { enum: ["past", "yuqori"] };
console.log(enumFarq(eski, yangi, "javob"));
console.log(enumFarq(eski, yangi, "sorov"));
console.log(enumFarq(eski, kichik, "sorov"));
console.log(enumFarq(eski, kichik, "javob"));Konsolda:
[ "BUZADI: qo'shildi shoshilinch" ]
[ 'xavfsiz' ]
[ 'BUZADI: olib tashlandi orta' ]
[ 'xavfsiz' ]Javobdagi yangi enum qiymati ko'pincha "kichik o'zgarish" deb o'tkazib yuboriladi — lekin mobil ilova eski versiyasida switch ning default qismi bo'lmasa, ekran bo'sh qoladi. Shuning uchun ba'zi jamoalar javobdagi enum'larni "kengayishi mumkin" deb hujjatlaydi va mijozdan noma'lum qiymatni ko'tarishni talab qiladi.
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Kurs rejasi bo'yicha OpenAPI vazifalar-api ga qo'shilmaydi. Kanon zanjirni tajribada yurgizdi: Zod → zod-to-openapi → 1 141 baytlik hujjat → openapi-typescript tiplari → oddiy breaking-change tekshiruvi. Loyihada esa hujjat 30-qismda NestJS'ning Swagger moduli bilan avtomatik paydo bo'ladi — u ham code-first, faqat Zod o'rniga dekoratorlar (yoki Zod adapteri). Bu darsdagi g'oyalar — operationId, tip generatsiyasi, breaking change — o'sha yerda to'g'ridan ishlatiladi.
O'zingiz sinab ko'ring: alohida tajriba papkasida npm i zod@4.6.7 @asteasolutions/zod-to-openapi@9.1.0 openapi-typescript@7.13.0. vazifalar-api dagi Zod sxemalaringizni nusxalab, ikkita amal (GET va POST) uchun hujjat yasang. npx openapi-typescript openapi.json -o api-tiplari.d.ts bilan tiplarni chiqarib, frontend'dagi qo'lda yozilgan Vazifa tipi bilan solishtiring.
11. Real ishda
- TS monorepo (Next.js frontend + Express/Fastify backend) — code-first hujjat va generatsiya qilingan tiplar eng ko'p uchraydigan juftlik.
- Mobil jamoa — Kotlin/Swift klientlarini OpenAPI Generator bilan yasaydi; hujjat sifati ular uchun hayotiy.
- Ommaviy API (to'lov, logistika) — Scalar yoki Redoc sahifasi, Spectral qoidalari, har relizda diff.
- NestJS —
@nestjs/swaggerdekoratorlardan hujjat (30-qism). - Intervyuda: "Code-first va spec-first farqi?", "Frontend tiplarini backend bilan qanday sinxron ushlaysiz?", "Qaysi o'zgarishlar breaking?"
Xulosa
- Code-first: Zod sxemasi = validatsiya + hujjat;
reg.registersxemanicomponentsga,registerPathamalnipathsga yozadi. z.toJSONSchema— bitta sxema uchun; butun API hujjati uchun registry kerak; natijalari biroz farqli (anyOf,additionalProperties)./openapi.json— mashina uchun,/docs(Swagger UI, Scalar, Redoc) — odam uchun; CSP va prod'dagi "Try it out" ni unutmang.openapi-typescript— tiplar;openapi-fetch, orval, hey-api — klient; generatsiya qilingan kod qo'lda tahrirlanmaydi.- Breaking change yo'nalishga bog'liq: javobdan olib tashlash, so'rovda yangi majburiy maydon.
- Spectral, oasdiff va kontrakt test — CI'dagi himoya; ular kanonda sinalmagan.
Keyingi dars: GraphQL asoslari — mijoz o'ziga kerakli maydonlarni o'zi so'raydigan API: sxema, Query, Mutation va resolver'lar.
Manbalar
@asteasolutions/zod-to-openapi: https://github.com/asteasolutions/zod-to-openapi- Zod — JSON Schema'ga o'tkazish: https://zod.dev/json-schema
- openapi-typescript va openapi-fetch: https://openapi-ts.dev/
- orval: https://orval.dev/ · hey-api: https://heyapi.dev/
- Swagger UI: https://swagger.io/tools/swagger-ui/ · Scalar: https://scalar.com/ · Redoc: https://redocly.com/docs/redoc
- Spectral: https://docs.stoplight.io/docs/spectral · oasdiff: https://github.com/oasdiff/oasdiff
- Kanon tajribasi (IlmHamroh, 2026-10-27): zod-to-openapi 9.1.0, zod 4.6.7, openapi-typescript 7.13.0, Node 24.21.0. To'liq hujjat, tiplar va
z.toJSONSchemanatijasi — biz kanon kodi bilan qayta ishga tushirdik (2026-10-28); Swagger UI, Scalar, Redoc, orval, hey-api, Spectral, oasdiff — sinalmagan.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!