Asosiy kontentga o‘tish

Maqolalar 6 daqiqa 45 ko‘rish

REST API'ni versiyalash: nega birinchi kundan /v1/ bilan boshlanadi

API'ingizni boshqa ilovalar ishlatishni boshlagach, unga har qanday o‘zgarish kiritish xavfli bo‘lib qoladi — bitta o‘zgarish yuzlab mijozni buzishi mumkin. Versiyalash shu muammoni yechadi va uni boshidan o‘ylagan ma'qul.

api backend

Mundarija

API bilan ishlashda bir narsani tez anglaysiz: uni chiqargan kuningizdan boshlab, u endi sizniki emas. To‘g‘rirog‘i, faqat sizniki emas. Uni mobil ilova ishlatadi, boshqa jamoaning serveri ishlatadi, balki hatto siz bilmagan odam ham ishlatadi. Va aynan shu paytdan boshlab API'ga har qanday o‘zgarish xavfli bo‘lib qoladi: siz bir joyni "yaxshilagan" kuningiz, o‘sha o‘zgarishga tayyor bo‘lmagan hamma mijoz birdan buziladi.

Buni tasavvur qilish uchun oddiy misol. Aytaylik, javobdagi bitta maydonni ikkiga bo‘lishga qaror qildingiz:

JSON
// Eski javob
{"name": "Ali Valiyev"}

// Yangi javob
{"first_name": "Ali", "last_name": "Valiyev"}

Sizning nazaringizda bu yaxshilanish. Lekin name maydonini kutib turgan barcha eski mijozlar endi bo‘sh joy oladi va ular uchun ilova buziladi. Siz esa ularning kodini o‘zgartira olmaysiz — u boshqa odamlarning qo‘lida.

Versiyalash bu muammoni qanday yechadi#

G‘oya oddiy: eski javobni saqlab qolib, yangisini alohida versiyada chiqarasiz. Eski mijozlar v1 bilan ishlashda davom etadi, yangi imkoniyat esa v2 da paydo bo‘ladi. Hech kim buzilmaydi, siz esa rivojlanishda davom etasiz.

Ikki asosiy usul#

Eng ko‘p tarqalgan va eng sodda usul — versiyani URL'ga qo‘yish:

Misol
GET /api/v1/users/
GET /api/v2/users/

Bu usulning yaxshi tomoni shundaki, versiya ko‘rinib turadi. Uni log'da o‘qib bo‘ladi, brauzerda ochib ko‘rish mumkin, boshqa jamoaga tushuntirish oson. Aynan shu ko‘rinuvchanlik uchun amaliyotda ko‘pchilik shu yo‘lni tanlaydi.

Ikkinchi usul — versiyani so‘rov sarlavhasiga yashirish:

Misol
GET /api/users/
Accept: application/vnd.myapp.v2+json

Bu "akademik jihatdan toza" hisoblanadi, chunki URL o‘zgarmaydi. Lekin versiya endi ko‘zga ko‘rinmaydi: uni curl bilan sinash noqulay, xatoni tekshirish qiyinroq. Amalda soddaligi uchun URL usuli deyarli har doim yutadi.

Qachon yangi versiya kerak — va qachon kerak emas#

Bu yerda chegara aniq. Yangi versiya faqat buzuvchi o‘zgarish uchun ochiladi. Buzuvchi o‘zgarish deganda — maydonni o‘chirish yoki nomini o‘zgartirish, javob strukturasini qayta qurish, yoki yangi majburiy parametr qo‘shish nazarda tutiladi. Bularning har biri eski mijozni buzadi, shuning uchun yangi versiya talab qiladi.

Buzmaydigan o‘zgarishlar uchun esa yangi versiya umuman shart emas. Javobga yangi, ixtiyoriy maydon qo‘shish hech kimni buzmaydi — eski mijozlar uni shunchaki e'tiborsiz qoldiradi. Yangi endpoint qo‘shish ham xuddi shunday: eskilariga tegmaydi. Ya'ni har o‘zgarishda versiya oshiravermang, faqat haqiqatan buzadigan o‘zgarishlarda.

Django'da buni qanday qilasiz#

Gap nazariyada oson, shuning uchun Django Rest Framework misolida ko‘rsatay — u versiyalashni o‘zi qo‘llab-quvvatlaydi, qo‘lda yozish shart emas.

Avval sozlamada qaysi usul ishlatilishini va qaysi versiyalar mavjudligini aytamiz:

Python
# config/settings.py
REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
    "ALLOWED_VERSIONS": ["v1", "v2"],
    "DEFAULT_VERSION": "v1",
}

Keyin manzilda versiya uchun joy ochamiz:

Python
# config/urls.py
urlpatterns = [
    path("api/<str:version>/users/", UserList.as_view()),
]

Endi view ichida request.version orqali qaysi versiya so‘ralayotganini bilasiz va bitta view ikkala versiyaga ham javob bera oladi:

Python
# apps/users/views.py
from rest_framework.response import Response
from rest_framework.views import APIView

FOYDALANUVCHILAR = [{"first_name": "Ali", "last_name": "Valiyev"}]


class UserList(APIView):
    # `version` yo'ldan kwargs bo'lib keladi, shuning uchun **kwargs kerak
    def get(self, request, *args, **kwargs):
        if request.version == "v1":
            eski = [{"name": f"{u['first_name']} {u['last_name']}"} for u in FOYDALANUVCHILAR]
            javob = Response(eski)
            javob["Deprecation"] = "@1767657600"  # 2026-01-06 dan eskirgan
            javob["Sunset"] = "Wed, 01 Jul 2026 00:00:00 GMT"
            return javob
        return Response(FOYDALANUVCHILAR)

Uchta manzilni so‘rab ko‘ramiz:

Natija
/api/v1/users/
   200 [{"name":"Ali Valiyev"}]
   Deprecation: @1767657600
   Sunset: Wed, 01 Jul 2026 00:00:00 GMT
/api/v2/users/
   200 [{"first_name":"Ali","last_name":"Valiyev"}]
/api/v3/users/
   404 {"detail":"Invalid version in URL path."}

Uch narsaga e'tibor bering. Birinchisi — kichik, lekin ko‘pchilikni qoqintiradigan tafsilot: <str:version> yo‘l parametri view'ga oddiy argument bo‘lib ham uzatiladi, shuning uchun def get(self, request) deb yozsangiz TypeError: UserList.get() got an unexpected keyword argument 'version' olasiz.

Ikkinchisi — ALLOWED_VERSIONS ni yozishni unutmaslik kerak. U bo‘lmasa DRF /api/v3/ ni ham qabul qiladi va request.version ga oddiygina "v3" deb qo‘yadi; yuqoridagi kod esa else shoxiga tushib, 200 bilan v2 javobini qaytaradi. Ya'ni mijoz manzilda xato qilgan, lekin buni hech kim bilmaydi. ALLOWED_VERSIONS bilan esa toza 404 keladi.

Uchinchisi — v1 javobidagi ikkita sarlavha. Ular o‘ylab topilgan emas, standartlashtirilgan: Deprecation (RFC 9745) versiya qachondan eskirgan deb hisoblanishini, Sunset (RFC 8594) esa u qachon butunlay o‘chishini aytadi. Ikkisining formati boshqacha — Deprecation da Unix vaqti oldiga @ qo‘yiladi, Sunset da esa odatdagi HTTP sanasi turadi. Foydasi shundaki, mijoz jamoasi hujjatni o‘qimasa ham, javobning o‘zida ogohlantirishni ko‘radi — va monitoringga "eskirgan versiyaga necha so‘rov kelyapti" degan o‘lchovni qo‘yib qo‘yish mumkin.

Bir narsani qo‘shib qo‘yay: yuqoridagi if request.version == "v1" — misol uchun qulay, lekin ikki-uch maydondan ko‘pi farq qila boshlaganda view ichi chalkashadi. Amalda har versiyaga alohida serializer yoziladi va view faqat qaysi birini ishlatishni tanlaydi.

Amaliy tavsiyalar#

Bir necha oddiy qoida ishni osonlashtiradi. Eski versiyani yangisi chiqishi bilanoq o‘chirmang — mijozlarga ko‘chib o‘tishga vaqt bering, chunki ular buni bir kunda qila olmaydi. O‘chirishdan oldin esa ogohlantiring — hujjatda va yuqorida ko‘rgan Deprecation/Sunset sarlavhalarida. Va ko‘p versiyani bir vaqtda cheksiz saqlamang — har biri qo‘shimcha yuk, odatda ikkitasini birga qo‘llab-quvvatlash yetarli.

Oxirgi va eng amaliy maslahat: versiyalashni birinchi kundan o‘ylang. Hatto eng birinchi versiyani ham /v1/ deb chiqaring, garchi hali hech qanday /v2/ bo‘lmasa ham. Bu bir belgining o‘zi — lekin ertaga API'ni o‘zgartirish kerak bo‘lganda, o‘sha bir belgi sizni katta boshog‘riqdan qutqaradi. Buni kelajakdagi o‘zingizga bergan sovg‘a deb bilib qo‘ying.

6 daqiqa 45 marta o‘qildi