Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Buzuvchi va buzmaydigan o'zgarish
- 2.1 Ikki toifa
- 2.2 Yulduzcha: "tolerant o'quvchi"
- 3. Versiya qayerda turadi
- 3.1 Uch usul
- 3.2 Kanon tanlovi — URI
- 4. Eskirish: Deprecation, Sunset va voris havolasi
- 4.1 Uchta sarlavha
- 4.2 Qiymatlarni hisoblash
- 4.3 Mijoz ogohlantirishni o'qiydi
- 5. Hayot sikli va changelog
- 5.1 Yo'lning hayoti
- 5.2 Changelog
- 6. API qadami: /v1 va eskirgan yo'llar (holat 26, 1-qism)
- 6.1 Nega aynan hozir
- 6.2 src/ilova.ts — v1 router
- 6.3 src/umumiy/eskirgan.ts
- 6.4 CORS
- 6.5 Natija (kanon curl, holat 26)
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): buzadimi?
- 2-mashq (o'rta): sarlavhalarni yasash
- 3-mashq (qiyin): bir marta ogohlantiruvchi mijoz
- 4-mashq: API qadami
- 9. Real ishda
- Xulosa
- Manbalar
API versiyalash va deprecation: /v1, buzuvchi o'zgarish, Deprecation va Sunset sarlavhalari
Qisqacha: API'ni o'zgartirganda eski mijozlar ishlashda davom etishi kerak. Yangi ixtiyoriy maydon yoki yangi endpoint — buzmaydigan o'zgarish, versiya kerak emas. Maydonni o'chirish, nomini yoki turini o'zgartirish, xato formatini almashtirish — buzuvchi o'zgarish: yangi versiya (
/v2) chiqariladi, eskisi bir muddat yashaydi. Eski yo'l javobigaDeprecation(RFC 9745) — "qachondan eskirgan",Sunset(RFC 8594) — "qachon o'chadi" vaLink: <…>; rel="successor-version"qo'yiladi. O'zgarishlar tarixi — changelog'da.
Bu darsda
- Qaysi o'zgarish mijozni buzadi, qaysi biri buzmasligini ajrata olasiz.
- Versiyani URI, sarlavha yoki media turida berishning afzallik va kamchiliklarini taqqoslaysiz.
Deprecation,Sunsetvasuccessor-versionsarlavhalarini to'g'ri formatda qo'yasiz va mijozda o'qiysiz.- Changelog va "eskirish muddati" bilan API hayot siklini rejalashtirasiz.
vazifalar-apini/v1ga ko'chirasiz va eski yo'llarni eskirgan deb belgilaysiz.
Oldin bilishingiz kerak: REST prinsiplari va resurslar, Xato javoblari: Problem Details, express.Router va qatlamli loyiha tuzilmasi, Semver va versiya diapazonlari.
1. Nega bu kerak?
Problem Details darsida xato formatini o'zgartirdik: { "xato": "…" } o'rniga { type, title, detail … }. Server uchun bu yaxshilanish. Lekin 22-qismdagi frontend xato maydonini o'qiydi — endi u xabarni topmaydi va foydalanuvchiga "Server xatosi: 422" ko'rsatadi. Biz frontend'ni buzdik.
Frontend o'zimizniki bo'lsa — uni ham tuzatamiz. Lekin API'ga telefon ilovasi ham ulangan deylik. Uning eski versiyasi Play Market'da, minglab telefonlarda o'rnatilgan va oylar davomida yangilanmaydi. Server bir lahzada o'zgaradi, mijozlar esa yo'q.
O'xshatish: avtobus yo'nalishi. Shahar 45-avtobus yo'nalishini o'zgartirmoqchi. Ertalab to'satdan o'zgartirsa — odamlar bekatda kutib qoladi. To'g'ri yo'li: yangi yo'nalishga yangi raqam (45A) beriladi, eski bekatlarda "bu yo'nalish 1-yanvardan yopiladi, 45A ga o'ting" e'loni osiladi. Versiya — yangi raqam; Deprecation va Sunset — bekatdagi e'lon.
2. Buzuvchi va buzmaydigan o'zgarish
2.1 Ikki toifa
Buzuvchi o'zgarish (breaking change) — eski, o'zgartirilmagan mijozni ishdan chiqaradigan o'zgarish. Buzmaydigan (non-breaking) — eski mijoz sezmaydi.
| O'zgarish | Toifa | Nega |
|---|---|---|
javobga yangi maydon (versiya) |
buzmaydi* | eski mijoz uni o'qimaydi |
yangi endpoint (/eksportlar) |
buzmaydi | eski mijoz uni chaqirmaydi |
| so'rovga yangi IXTIYORIY parametr | buzmaydi | yubormasa — sukut ishlaydi |
| maydonni o'chirish yoki nomini o'zgartirish | buzadi | mijoz undefined oladi |
maydon turini o'zgartirish (id: son → satr) |
buzadi | id + 1 endi "71" |
| so'rovga yangi MAJBURIY maydon | buzadi | eski so'rov endi 422 |
| xato formatini almashtirish | buzadi | 22-qism frontend'i kabi |
| status kodni o'zgartirish (200 → 201) | buzadi | status === 200 tekshiruvi |
| enum'ga yangi qiymat | ehtiyot | mijoz switch ida default bo'lmasa |
2.2 Yulduzcha: "tolerant o'quvchi"
Yangi maydon faqat mijoz noma'lum maydonlarga chidamli bo'lsa buzmaydi. Bu qoida tolerant o'quvchi (tolerant reader) deb ataladi: "o'zing kutganini o'qi, qolganini e'tiborsiz qoldir". Quyidagi brauzer blokida ikkita mijoz bir xil yangi javobni o'qiydi:
const oldShape = ["id", "matn", "bajarildi"];
function strictRead(task) {
const keys = Object.keys(task);
const extra = keys.filter((k) => !oldShape.includes(k));
if (extra.length) throw new Error(`noma'lum maydon: ${extra}`);
return task;
}
function tolerantRead(task) {
return { id: task.id, matn: task.matn, bajarildi: task.bajarildi };
}
// server javobiga yangi "versiya" maydoni qo'shildi
const fromServer = {
id: 7, matn: "Non olish", bajarildi: false, versiya: 2,
};
console.log(tolerantRead(fromServer));
try {
strictRead(fromServer);
} catch (error) {
console.log(error.message);
}Konsolda:
{ id: 7, matn: 'Non olish', bajarildi: false }
noma'lum maydon: versiyaQattiq mijoz buzildi — server faqat maydon QO'SHGAN bo'lsa ham. Bu real tuzoq: Zod'dagi z.strictObject ni javobni tekshirishga ishlatsangiz, xuddi shunday bo'ladi. So'rovni qattiq tekshirish — to'g'ri (server himoyasi). Javobni o'qishda esa oddiy z.object (ortiqchani tashlab yuboradi) — to'g'ri.
Tekshirib ko'ring: Server
bajarildi: booleano'rnigaholat: "yangi" | "bajarildi" | "bekor"qo'ymoqchi vabajarildini olib tashlamoqchi. Buzuvchimi? Buzmaydigan qilib qanday chiqarish mumkin?
Javob
Buzuvchi — bajarildi o'chdi. Buzmaydigan yo'li: holat ni QO'SHING, bajarildi ni ham qoldiring (holat === "bajarildi" dan hisoblanadi). Eski mijozlar bajarildi ni o'qishda davom etadi, yangilari holat ni. bajarildi ni o'chirish — keyingi katta versiyada (/v2), eskirish e'loni bilan.
3. Versiya qayerda turadi
3.1 Uch usul
| Usul | Misol | Afzallik | Kamchilik |
|---|---|---|---|
| URI | GET /v1/vazifalar |
ko'rinadi, brauzer va curl'da oson, kesh URI bo'yicha ajratadi | "resurs bitta, URI ikkita" — REST nazariyasiga to'liq mos emas |
| Sarlavha | X-API-Version: 2 yoki Api-Version: 2026-10-24 |
URI toza | brauzerda sinash qiyin, CORS ro'yxatiga kerak, kesh Vary siz adashadi |
| Media turi | Accept: application/vnd.vazifa.v2+json |
HTTP g'oyasiga eng yaqin | eng murakkab; ko'p vosita qo'llamaydi |
Katta kompaniyalar turlicha tanlagan:
- Google (API Improvement Proposals, 185-band) — asosiy versiya URI'da:
/v1/…. - GitHub REST — sana bilan sarlavhada:
X-GitHub-Api-Version: 2022-11-28. - Stripe — sana bilan sarlavhada (
Stripe-Version), har akkauntning sukut versiyasi bor.
Sana bilan versiyalash — kichik, tez-tez o'zgarishlar uchun. URI'dagi v1 — kamdan-kam, katta o'zgarishlar uchun.
3.2 Kanon tanlovi — URI
vazifalar-api versiyani URI'da beradi: /v1/vazifalar. Sabablar (kanon QADAMLAR):
- O'quvchi brauzer va curl bilan sinaydi — sarlavha qo'shish shart emas.
- CORS'da qo'shimcha sarlavha kerak emas.
- Keshlar (brauzer, CDN) URI bo'yicha ishlaydi —
Varyhaqida o'ylash shart emas (HTTP kesh darsi).
Faqat asosiy (major) raqam URI'da. Semver darsida ko'rgan 1.4.2 — paketlar uchun. API'da v1.4 yo'q: buzmaydigan o'zgarishlar v1 ichida jim qo'shiladi, faqat buzuvchisi v2 ni talab qiladi.
Maslahat:
v2chiqarish — qimmat qaror: ikki versiyani bir vaqtda qo'llab-quvvatlash, ikki xil test, ikki xil hujjat. Avval buzmaydigan yo'lni qidiring (yangi maydon qo'shish, eskisini qoldirish).v2— oxirgi chora.
4. Eskirish: Deprecation, Sunset va voris havolasi
4.1 Uchta sarlavha
Eski yo'l darhol o'chirilmaydi. U ishlashda davom etadi, lekin har javobda ogohlantirish sarlavhalari bo'ladi:
HTTP/1.1 200 OK
Deprecation: @1792800000
Sunset: Sun, 24 Jan 2027 00:00:00 GMT
Link: </v1/vazifalar/1>; rel="successor-version"| Sarlavha | Standart | Ma'nosi | Format |
|---|---|---|---|
Deprecation |
RFC 9745 (2025-03) | shu sanadan boshlab eskirgan | @ + Unix vaqti (soniya) |
Sunset |
RFC 8594 (2019-05) | shu sanadan keyin ishlamasligi mumkin | HTTP sana (Sun, 24 Jan 2027 00:00:00 GMT) |
Link … rel="successor-version" |
RFC 5829 | o'rniga nima ishlatish | URI burchak qavslarda |
Eskirgan (deprecated) — "ishlaydi, lekin ishlatmang, o'rnini bosuvchisi bor". Sunset (quyosh botishi) — "shu kundan keyin o'chiriladi". Ikkalasi alohida: biri ogohlantirish, ikkinchisi muddat.
Ikki sarlavhaning formati har xil ekaniga e'tibor bering. Deprecation yangiroq standart va u "structured field" sanasi: @ va soniyalar. Eski qoralamalarda Deprecation: true ko'rinishi uchraydi — RFC 9745 da u yo'q.
4.2 Qiymatlarni hisoblash
Bu blok brauzerda ham ishlaydi:
const deprecatedAt = Date.UTC(2026, 9, 24); // oy 0 dan: 9 = oktabr
const sunsetAt = Date.UTC(2027, 0, 24);
console.log(`Deprecation: @${deprecatedAt / 1000}`);
console.log(`Sunset: ${new Date(sunsetAt).toUTCString()}`);
function daysLeft(sunsetHeader, now) {
const ms = Date.parse(sunsetHeader) - now;
return Math.ceil(ms / 86_400_000);
}
const now = Date.UTC(2026, 11, 1);
console.log(daysLeft(new Date(sunsetAt).toUTCString(), now));
console.log(new Date(Number("1792800000") * 1000).toISOString());Konsolda:
Deprecation: @1792800000
Sunset: Sun, 24 Jan 2027 00:00:00 GMT
54
2026-10-24T00:00:00.000ZDate.UTC oyni 0 dan sanaydi — 9 oktabr, 0 yanvar (Date asoslari). Date.now() millisekund beradi, Deprecation esa soniya kutadi — 1000 ga bo'lish shart. toUTCString() aynan HTTP sana formatini beradi. 2026-12-01 dan Sunset'gacha — 54 kun.
4.3 Mijoz ogohlantirishni o'qiydi
Sarlavha qo'yish yarim ish. Mijoz uni o'qib, dasturchiga ko'rsatishi kerak — odatda logga yoki dev konsolga. Quyidagi blok Node uchun:
import { createServer } from "node:http";
const OLD = ["/vazifalar", "/teglar"];
const server = createServer((req, res) => {
const path = req.url;
const old = OLD.find((p) => path === p || path.startsWith(p + "/"));
if (old) {
res.setHeader("Deprecation", "@1792800000");
res.setHeader("Sunset", "Sun, 24 Jan 2027 00:00:00 GMT");
res.setHeader("Link", `</v1${path}>; rel="successor-version"`);
}
const one = { id: 1, matn: "Non olish" };
const body = path.endsWith("/1") ? one : [];
res.setHeader("content-type", "application/json");
res.end(JSON.stringify(body));
});
await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
const base = `http://127.0.0.1:${server.address().port}`;
async function get(path) {
const res = await fetch(base + path);
if (res.headers.has("deprecation")) {
const sunset = res.headers.get("sunset");
const link = res.headers.get("link");
console.log(`OGOH: ${path} eskirgan`);
console.log(` o'chadi: ${sunset}`);
console.log(` voris: ${link}`);
}
return res.json();
}
console.log(await get("/v1/vazifalar/1"));
console.log(await get("/vazifalar/1"));
server.closeAllConnections();
server.close();Konsolda:
{ id: 1, matn: 'Non olish' }
OGOH: /vazifalar/1 eskirgan
o'chadi: Sun, 24 Jan 2027 00:00:00 GMT
voris: </v1/vazifalar/1>; rel="successor-version"
{ id: 1, matn: 'Non olish' }Ikkala yo'l bir xil ma'lumot qaytaradi — eski yo'l ishlayapti. Farq faqat sarlavhalarda. Mijoz ogohlantirishni bir marta ko'rsatishi yetarli (har so'rovda logni to'ldirmaslik uchun — buni 3-mashqda qilasiz).
Tekshirib ko'ring: Nega
Deprecationda millisekund (1792800000000) yozish xato?
Javob
RFC 9745 Unix vaqtini soniyada kutadi. Millisekund bilan mijoz uni taxminan 58 800-yildagi sana deb o'qiydi — ogohlantirish ma'nosiz bo'ladi. JS'da Date.now() va Date.UTC() millisekund qaytaradi, shuning uchun / 1000 (va kerak bo'lsa Math.floor).
5. Hayot sikli va changelog
5.1 Yo'lning hayoti
Eski yo'l qanday bosqichlardan o'tadi — diagrammadan kuzating:
stateDiagram-v2
[*] --> Faol
Faol --> Eskirgan: v1 chiqdi (Deprecation)
Eskirgan --> Ochirilgan: Sunset sanasi o'tdi
Ochirilgan --> [*]
note right of Eskirgan
ishlaydi, lekin har javobda
Deprecation + Sunset + Link
end noteMuddat qancha bo'lsin? Qoida yo'q, lekin mijozlar kimligiga bog'liq:
| Mijozlar | Odatiy muddat |
|---|---|
| faqat o'z frontend'ingiz | 1–4 hafta |
| o'z mobil ilovangiz | 3–6 oy (eski versiyalar telefonlarda qoladi) |
| tashqi dasturchilar (ochiq API) | 6–24 oy |
Kanon 3 oy tanlagan: Sunset — 2027-01-24. Sunset'dan keyin eski yo'l odatda 410 Gone qaytaradi ("bu resurs bor edi va ataylab olib tashlandi") — 404 dan aniqroq. Kanonda bu bosqich hali yo'q: u 2027-yanvarda keladi.
5.2 Changelog
Changelog (o'zgarishlar tarixi) — har versiyada nima o'zgargani yozilgan hujjat. Sarlavhalar mashina uchun, changelog — odam uchun. Unda uch narsa bo'lsin: sana, o'zgarish, buzuvchimi yoki yo'q. Kanondagi API.md (keyingi darslarda yoziladi) shunday tugaydi:
## O'zgarishlar tarixi (changelog)
- v1 (2026-10-24): /v1 prefiksi; xatolar Problem Details;
ro'yxat — { versiya, vazifalar, sahifa }; versiya maydoni
(ETag); eksport, toplam. Eski versiyasiz yo'llar
(/vazifalar, /teglar) — eskirgan (Deprecation),
2027-01-24 da o'chiriladi (Sunset).Real loyihalarda yana uchta kanal ishlatiladi: email ro'yxati ("API yangiliklari"), hujjat sahifasidagi banner va eskirgan yo'lni chaqirayotgan mijozlarni logdan topib, ularga alohida yozish.
6. API qadami: /v1 va eskirgan yo'llar (holat 26, 1-qism)
Bu qadam holat 24 dan boshlanadi. Commit — keyingi darsda, HTTP kesh bilan birga (holat 26). Hozircha commit qilmang.
6.1 Nega aynan hozir
Holat 24 da xato formati o'zgardi — bu buzuvchi o'zgarish. Kanon qarori: v1 aynan shu formatdan boshlanadi. Eski versiyasiz yo'llar ham ishlaydi (frontend birdan sinmasin), lekin eskirgan deb belgilanadi.
6.2 src/ilova.ts — v1 router
// Versiya URI'da: /v1/... Buzuvchi o'zgarish bo'lsa — /v2,
// v1 bir muddat yashaydi
const v1 = express.Router();
v1.use("/vazifalar", idempotentlik(idempotentlikOmbori),
vazifalarRouteri(xizmat));
v1.use("/teglar", teglarRouteri(xizmat));
app.use("/v1", v1);
// Eski versiyasiz yo'llar — ishlaydi, lekin eskirgan:
// Deprecation + Sunset + vorisga havola
app.use(eskirgan(ESKI_YOLLAR), v1);Bitta v1 router ikki joyga ulandi. /v1/vazifalar — oddiy. /vazifalar esa avval eskirgan middleware'idan o'tadi, keyin xuddi o'sha router'ga tushadi (Router darsi). Mantiq takrorlanmaydi — ikkala yo'l bir xil kodni ishlatadi.
Ikki so'rovni yonma-yon kuzating:
| Qadam | Middleware | GET /v1/vazifalar/1 |
GET /vazifalar/1 |
|---|---|---|---|
| 1 | app.use("/v1", v1) |
mos — v1 ga kiradi |
mos emas — o'tkazib yuboriladi |
| 2 | eskirgan(ESKI_YOLLAR) |
yetib kelmaydi | /vazifalar prefiksi topildi — 3 sarlavha qo'yildi |
| 3 | v1 router → vazifalarRouteri |
200 + vazifa | 200 + xuddi o'sha vazifa |
Farq faqat ikkinchi qadamda: eski yo'l javobiga ogohlantirish sarlavhalari qo'shiladi.
6.3 src/umumiy/eskirgan.ts
Yangi fayl — to'liq (kanondan; qatorlar telefon uchun bo'lingan):
// eskirgan.ts — eskirgan (deprecated) yo'llar: ishlaydi, lekin har
// javobda ogohlantirish sarlavhalari. Deprecation (RFC 9745):
// qachondan eskirgan (@unix-vaqt); Sunset (RFC 8594): qachon
// o'chiriladi; Link rel="successor-version"
import type { RequestHandler } from "express";
export type EskiYol = { prefiks: string; voris: string };
export const ESKI_YOLLAR: readonly EskiYol[] = [
{ prefiks: "/vazifalar", voris: "/v1/vazifalar" },
{ prefiks: "/teglar", voris: "/v1/teglar" },
];
/** 2026-10-24 00:00 UTC — /v1 chiqqan kun */
export const ESKIRGAN_VAQT = Date.UTC(2026, 9, 24);
/** 3 oydan keyin o'chiriladi */
export const QUYOSH_BOTISHI = Date.UTC(2027, 0, 24);
export function eskirgan(yollar: readonly EskiYol[]): RequestHandler {
return (req, res, next) => {
const y = yollar.find((e) =>
req.path === e.prefiks || req.path.startsWith(e.prefiks + "/"));
if (y !== undefined) {
res.set("Deprecation", `@${String(ESKIRGAN_VAQT / 1000)}`);
res.set("Sunset", new Date(QUYOSH_BOTISHI).toUTCString());
const qolgan = req.path.slice(y.prefiks.length);
res.append("Link",
`<${y.voris}${qolgan}>; rel="successor-version"`);
}
next();
};
}Uch nozik joy:
startsWith(e.prefiks + "/")—/vazifalarimkabi boshqa yo'l tasodifan mos kelmasin.res.append("Link", …)—setemas: sahifalash darsidagirel="next"havolasi hamLinksarlavhasida.appendikkalasini saqlaydi.- Sanalar konstanta — har so'rovda yangi hisoblanmaydi va test ularni aniq solishtira oladi.
6.4 CORS
Brauzer fetch i javob sarlavhalarining faqat "oddiy" qismini o'qiy oladi (CORS mijoz tomondan). Deprecation, Sunset, Link ni frontend ko'rishi uchun ular exposedHeaders ro'yxatiga qo'shildi (keyingi darsdagi ETag bilan birga):
exposedHeaders: ["Location", "X-Request-Id", "Idempotency-Replayed",
"ETag", "Link", "Deprecation", "Sunset"],6.5 Natija (kanon curl, holat 26)
curl -i $A/vazifalar/1HTTP/1.1 200 OK
Deprecation: @1792800000
Sunset: Sun, 24 Jan 2027 00:00:00 GMT
Link: </v1/vazifalar/1>; rel="successor-version"
Content-Type: application/json; charset=utf-8
{"id":1,"matn":"Non olish #uy","bajarildi":true,"teglar":["uy"],…}Hamma testlar endi /v1/... ga o'tkaziladi, eski yo'l uchun alohida test — sarlavhalar borligini tekshiradi. Holat 26 dagi ETag va versiya maydoni — keyingi darsda.
7. Ko'p uchraydigan xatolar
1. "Kichik" o'zgarishni versiyasiz chiqarish. "Faqat maydon nomini bajarildi dan tugadi ga o'zgartirdim" — eng ko'p uchraydigan buzilish. Nom o'zgarishi = o'chirish + qo'shish.
2. Har o'zgarishga yangi versiya. v1, v2, … v9 — ularni qo'llab-quvvatlab bo'lmaydi. Buzmaydigan o'zgarishlar v1 ichida.
3. Eskirgan yo'lni ogohlantirishsiz o'chirish. Sunset'siz o'chirish — mijozlar uchun to'satdan 404.
4. Deprecation da millisekund yoki true. RFC 9745: @ + soniya.
5. Mijozda javobni qattiq tekshirish. z.strictObject bilan javob o'qisa, server maydon qo'shganda mijoz buziladi. Javob — tolerant o'qiladi.
6. Link ni set bilan qo'yish. Boshqa havola (rel="next") o'chib ketadi.
8. Mashqlar
Yechimlar kurs/mashqlar/25/25-versiyalash/ da.
1-mashq (oson): buzadimi?
Har o'zgarish uchun "buzadi" yoki "buzmaydi" yozing:
- Javobga
yaratuvchimaydoni qo'shildi — GET /teglarendi[{ teg, soni }]o'rniga{ teglar: [...] }qaytaradi —POST /vazifalarga ixtiyoriymuddatmaydoni qo'shildi —limitsukut qiymati 20 dan 50 ga o'zgardi —DELETEmuvaffaqiyatda 204 o'rniga 200 qaytaradi —
Yechim
- buzmaydi — tolerant o'quvchi uni e'tiborsiz qoldiradi.
- buzadi — javob shakli o'zgardi: massiv o'rniga obyekt.
- buzmaydi — eski so'rov uni yubormaydi, sukut ishlaydi.
- buzadi — ko'zga ko'rinmas, lekin mijoz 20 talik sahifaga moslangan bo'lishi mumkin (UI, sahifa soni). Sukut qiymatlar ham shartnoma.
- buzadi —
status === 204tekshiruvi yoki "tana yo'q" taxmini sinadi.
2-mashq (o'rta): sarlavhalarni yasash
deprecationHeaders(deprecatedIso, sunsetIso, successor) yozing. U uchta sarlavhali obyekt qaytarsin. Sunset Deprecation'dan oldin bo'lsa — Error tashlasin. Bu toza JS — «Ishga tushir» bilan ishlaydi.
Yechim
function deprecationHeaders(deprecatedIso, sunsetIso, successor) {
const dep = Date.parse(deprecatedIso);
const sun = Date.parse(sunsetIso);
if (Number.isNaN(dep) || Number.isNaN(sun)) {
throw new Error("sana noto'g'ri");
}
if (sun <= dep) {
throw new Error("Sunset Deprecation'dan keyin bo'lsin");
}
return {
Deprecation: `@${Math.floor(dep / 1000)}`,
Sunset: new Date(sun).toUTCString(),
Link: `<${successor}>; rel="successor-version"`,
};
}
console.log(deprecationHeaders(
"2026-10-24T00:00:00Z", "2027-01-24T00:00:00Z", "/v1/teglar",
));
try {
deprecationHeaders("2027-01-24", "2026-10-24", "/x");
} catch (error) {
console.log(error.message);
}Konsolda:
{
Deprecation: '@1792800000',
Sunset: 'Sun, 24 Jan 2027 00:00:00 GMT',
Link: '</v1/teglar>; rel="successor-version"'
}
Sunset Deprecation'dan keyin bo'lsin3-mashq (qiyin): bir marta ogohlantiruvchi mijoz
createApiClient(fetchFn, warn) yozing. U get(path) qaytarsin. Javobda Deprecation bo'lsa — warn ni chaqirsin, lekin har yo'l uchun bir marta. Link dan voris manzilini ajratib (< va > orasi) xabarga qo'shsin. Soxta fetch bilan sinang: bitta eskirgan yo'lni 3 marta, yangisini 1 marta so'rang.
Yechim
function createApiClient(fetchFn, warn) {
const warned = new Set();
return {
async get(path) {
const res = await fetchFn(path);
if (res.headers.has("deprecation") && !warned.has(path)) {
warned.add(path);
const link = res.headers.get("link") ?? "";
const re = /<([^>]+)>;\s*rel="successor-version"/;
const next = re.exec(link);
const sunset = res.headers.get("sunset") ?? "noma'lum";
warn(`${path} eskirgan (${sunset}) → ${next?.[1] ?? "?"}`);
}
return res.json();
},
};
}
const fakeFetch = async (path) => {
const headers = path.startsWith("/v1") ? {} : {
deprecation: "@1792800000",
sunset: "Sun, 24 Jan 2027 00:00:00 GMT",
link: `</v1${path}>; rel="successor-version"`,
};
return new Response("[]", { headers });
};
const warn = (m) => console.log("OGOH:", m);
const client = createApiClient(fakeFetch, warn);
for (let i = 0; i < 3; i++) await client.get("/teglar");
await client.get("/v1/teglar");
console.log("tugadi");Konsolda:
OGOH: /teglar eskirgan (Sun, 24 Jan 2027 00:00:00 GMT) → /v1/teglar
tugadiUch so'rov — bitta ogohlantirish. Regex Link ichidan faqat successor-version havolasini oladi: sarlavhada rel="next" ham bo'lishi mumkin.
4-mashq: API qadami
O'z vazifalar-api ingizda «API qadami» bo'limidagi o'zgarishlarni qiling: v1 router, eskirgan.ts, CORS exposedHeaders. Hamma testlarni /v1/... ga o'tkazing va bitta yangi test yozing: GET /vazifalar/1 javobida Deprecation: @1792800000, Sunset va Link: </v1/vazifalar/1>; rel="successor-version" bor; GET /v1/vazifalar/1 da Deprecation yo'q. npm run check toza bo'lsin. Commit qilmang — keyingi darsda.
9. Real ishda
- Telegram Bot API, GitHub, Stripe — hammasi changelog yuritadi. GitHub hujjatiga ko'ra (2026-10 holatiga), eski REST versiyasi yangisi chiqqandan keyin kamida 24 oy ishlaydi. Yopilishi yaqinlashganda javoblarda
DeprecationvaSunsetsarlavhalari paydo bo'ladi, yopilgandan keyin esa — 410 Gone. - Mobil ilovali kompaniyalarda (Uzum, Yandex Go) "minimal qo'llab-quvvatlanadigan versiya" ham bor: juda eski ilova serverdan "yangilang" javobini oladi.
- NestJS (30-qism) versiyalashni o'zi qo'llaydi:
app.enableVersioning({ type: VersioningType.URI })— URI, sarlavha yoki media turi. - OpenAPI hujjatida (
deprecated: true) eskirgan operatsiya chizilgan holda ko'rinadi (OpenAPI darsi). - Intervyu savoli: "API'ni mijozlarni buzmasdan qanday o'zgartirasiz?" — buzmaydigan o'zgarishlar, tolerant o'quvchi, faqat major versiya URI'da, Deprecation/Sunset, changelog.
Xulosa
- Yangi maydon, endpoint yoki ixtiyoriy parametr — buzmaydi; o'chirish, nomni/turni/statusni/formatni o'zgartirish — buzadi.
- Javobni tolerant o'qing; so'rovni esa qattiq tekshiring.
- Versiya URI'da (
/v1) — eng sodda; faqat major raqam. - Eski yo'l darhol o'chmaydi:
Deprecation: @<soniya>,Sunset: <HTTP sana>,Link: <…>; rel="successor-version". - Changelog — odam uchun; sarlavhalar — mashina uchun; Sunset'dan keyin — 410 Gone.
Keyingi dars: HTTP caching va shartli so'rovlar — ETag, If-None-Match va 304 bilan trafikni tejaymiz, If-Match va 412 bilan esa ikki kishining bir vaqtdagi tahririda ma'lumot yo'qolishini to'xtatamiz.
Manbalar
- RFC 9745 "The Deprecation HTTP Response Header Field" (2025-03) — rfc-editor.org/rfc/rfc9745
- RFC 8594 "The Sunset HTTP Header Field" (2019-05) — rfc-editor.org/rfc/rfc8594
- RFC 5829 "Link Relation Types for Simple Version Navigation" (2010-04) — rfc-editor.org/rfc/rfc5829
- Google AIP 185 "API Versioning" — google.aip.dev/185
- GitHub REST API versions — docs.github.com/rest/about-the-rest-api/api-versions
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!