28 β Kapston: to'liq API'ni noldan loyihalash¶
β¬ οΈ Oldingi: 27 β Dizayn naqshlari va anti-naqshlari Β· π README
Bu bobda: 27 bob davomida biz API dizaynining alohida vositalarini o'rgandik β HTTP semantikasi, resurslar, status kodlar, auth, xato, versiyalash, OpenAPI, test. Endi ularning hammasini bitta jonli loyihada birlashtiramiz: noldan boshlab bitta real API'ni β vazifa-boshqaruvchi (task manager) ilovasini β o'n qadamli jarayon bo'yicha to'liq loyihalaymiz. Har qadamda tegishli bobga havola beramiz, qaror chiqaramiz va trade-off'ni asoslaymiz. Bob oxirida sizda noldan har qanday API'ni loyihalashga yetadigan tartib bo'ladi.
Halollik / Eslatma: Bu bob yangi standart kiritmaydi β u oldingi boblardagi standartlarni (HTTP semantikasi = RFC 9110, xato formati = RFC 9457, keshlash = RFC 9111, OpenAPI 3.1, OWASP API Security Top 10 β 2023) amalda qo'llaydi. Bitta "to'g'ri" API dizayni yo'q: bu yerdagi har bir qaror β kontekstga-bog'liq trade-off, va biz uni nima uchun shunday tanlaganimizni ochiq aytamiz. Barcha JSON namunalari valid (RFC 8259), OpenAPI parchasi 3.1 sintaksisida. Sizning loyihangizning talablari boshqacha bo'lsa, qarorlar ham boshqacha bo'lishi mumkin.
Kirish: vositalardan jarayongacha¶
Tasavvur qiling, siz endi quruvchisiz va sizda to'liq asboblar to'plami bor: bolg'a (HTTP metodlari), o'lchagich (status kodlar), poydevor quyish usuli (resurs dizayni), qulf (auth), signalizatsiya (rate limit), chizma standarti (OpenAPI). Asboblarni bilish β bir narsa. Ulardan uy qurish β boshqa narsa. Kapston shu haqida: bir-bir asbobni emas, balki butun jarayonni ko'rsatamiz.
Yaxshi API hech qachon "endpoint yozaman" deb klaviaturadan boshlanmaydi. U talabdan boshlanadi va spetsifikatsiyaga aylanadi, faqat keyin kodga. Bu bob shu tartibni β talabdan ishlaydigan shartnomagacha bo'lgan yo'lni β bitta misolda to'liq bosib o'tadi.
Mana umumiy jarayon. Uni yodda saqlang: keyin har qanday API'ni shu xarita bo'yicha loyihalashingiz mumkin.
Eslatma: Bu qadamlar qat'iy ketma-ket emas β ko'pincha ular takrorlanadi (iteratsiya). Endpoint dizayni paytida resurs modelini qayta ko'rib chiqasiz; OpenAPI yozayotganda xato formatini aniqlashtirasiz. Lekin birinchi marta o'tganda shu tartib eng tabiiy.
Qadam 1 β Talablar: nima va kim uchun¶
Hech bir texnik qaror talabni bilmasdan ma'noga ega emas. Avval savollarga javob beramiz:
API nima qiladi? Vazifa-boshqaruvchi (task manager). Foydalanuvchilar loyihalar (project) ochadi, ularning ichida vazifalar (task) yaratadi, vazifalarni boshqalarga biriktiradi, holatini o'zgartiradi (todo β doing β done), izoh (comment) qoldiradi.
Kim ishlatadi (mijoz turlari)? Bu narsa dizaynni keskin o'zgartiradi, shuning uchun aniqlaymiz:
| Mijoz | Xususiyati | Dizaynga ta'siri |
|---|---|---|
| Web SPA (brauzer) | Foydalanuvchi nomidan ishlaydi | Auth = OAuth Authorization Code + PKCE; CORS kerak |
| Mobil ilova | Foydalanuvchi nomidan, offline-ga moyil | Idempotentlik muhim (tarmoq beqaror); kompakt payload |
| Tashqi integratsiya (boshqa tizim) | Servis nomidan | Auth = Client Credentials; rate limit qattiqroq |
Asosiy use-case'lar (foydalanish stsenariylari):
- Foydalanuvchi loyiha yaratadi va unga vazifalar qo'shadi.
- Vazifani boshqa a'zoga biriktiradi va muddat (due date) belgilaydi.
- Vazifalarni holat/biriktirilgan-shaxs bo'yicha filtrlaydi va sahifalaydi.
- Vazifa holatini o'zgartiradi; izoh qoldiradi.
- Tashqi tizim webhook orqali "vazifa bajarildi" hodisasini oladi.
Funksional bo'lmagan talablar (NFR):
- Masshtab: ~10 ming faol foydalanuvchi, kuniga ~1 mln so'rov. Katta emas, lekin "kichik hobby" ham emas β multi-tenant.
- Xavfsizlik: har bir foydalanuvchi faqat o'zi a'zo bo'lgan loyihalarning vazifalarini ko'radi. Bu β eng muhim talab; uni e'tibordan chetda qoldirish OWASP API1: BOLA zaifligini keltiradi.
- Ishonchlilik: mobil mijoz bir so'rovni ikki marta yuborsa, ikkita vazifa yaratilmasligi kerak (idempotentlik).
- Evolyutsiya: API o'sadi; eski mijozlarni sindirmasdan yangilik qo'shish mumkin bo'lsin.
Trade-off: Talablarni yozish "ortiqcha rasmiyatchilik" tuyulishi mumkin, lekin aynan shu jadval keyingi har bir qarorni asoslaydi. "Kim ishlatadi" degan savolga javob bermasangiz, auth usulini tasodifan tanlaysiz.
Qadam 2 β Resurslarni aniqlash¶
Endi domendan (vazifa boshqaruvi) resurslarni ajratamiz. Qoida (6-bob: resurs va URI dizayni): resurslar odatda domendagi otlar β "narsalar", fe'llar emas. Use-case'lardagi otlarni belgilaymiz: foydalanuvchi, loyiha, vazifa, izoh, a'zolik.
| Resurs | Tavsif | Egasi/ota resursi |
|---|---|---|
users |
Foydalanuvchi profili | β (ildiz) |
projects |
Loyiha (vazifalar to'plami) | foydalanuvchilar a'zo |
tasks |
Vazifa | loyihaga tegishli |
comments |
Vazifa izohi | vazifaga tegishli |
members |
Loyiha a'zoligi (rol bilan) | loyihaga tegishli |
Resurslar orasidagi bog'lanishlar dizaynni belgilaydi:
- Loyiha β ko'p vazifa (
one-to-many): vazifa loyihasiz mavjud bo'lmaydi βtasksloyihaga uyalangan (nested). - Vazifa β ko'p izoh: izoh ham uyalangan.
- Foydalanuvchi β loyiha β ko'pdan-ko'p: oraliq resurs
members(rol bilan). - Vazifa β biriktirilgan foydalanuvchi: vazifaning
assignee_idmaydoni (havola, uyalash emas).
Trade-off β uyalash chuqurligi:
tasksloyihaga bog'liq, shuning uchun/projects/{id}/tasksmantiqiy. Lekin biz bir vazifaga to'g'ridan-to'g'ri ham murojaat qila olishimiz kerak (izoh, holat o'zgarishi). Yechim: yaratish/ro'yxat kolleksiya bo'yicha uyalangan (/projects/{pid}/tasks), bitta elementga murojaat esa tekis (flat) (/tasks/{tid}). Ikki-uch darajadan chuqur uyalashdan qoching:/projects/1/tasks/2/comments/3/likes/4o'qish va keshlash uchun og'ir.
Qadam 3 β Endpointlar: metod, URL, status¶
Endi har bir resurs uchun endpointlarni chizamiz. Har biri uch narsadan iborat: URL (resurs), metod (2-bob: HTTP chuqur), status kod (3-bob: status kodlar).
Metod tanlash qoidasi (RFC 9110): o'qish = GET (safe, idempotent); yangi element yaratish = POST (safe emas, idempotent emas); to'liq almashtirish = PUT (idempotent); qisman o'zgartirish = PATCH; o'chirish = DELETE (idempotent).
To'liq endpoint jadvali:
| Metod va URL | Maqsad | Muvaffaqiyat status | Auth (scope) |
|---|---|---|---|
POST /v1/projects |
Loyiha yaratish | 201 Created + Location |
projects:write |
GET /v1/projects |
Loyihalar ro'yxati | 200 OK |
projects:read |
GET /v1/projects/{pid} |
Bitta loyiha | 200 OK |
projects:read |
PATCH /v1/projects/{pid} |
Loyihani o'zgartirish | 200 OK |
projects:write |
DELETE /v1/projects/{pid} |
Loyihani o'chirish | 204 No Content |
projects:write |
POST /v1/projects/{pid}/tasks |
Vazifa yaratish | 201 Created + Location |
tasks:write |
GET /v1/projects/{pid}/tasks |
Vazifalar ro'yxati (filtr) | 200 OK |
tasks:read |
GET /v1/tasks/{tid} |
Bitta vazifa | 200 OK |
tasks:read |
PATCH /v1/tasks/{tid} |
Vazifani o'zgartirish | 200 OK |
tasks:write |
DELETE /v1/tasks/{tid} |
Vazifani o'chirish | 204 No Content |
tasks:write |
POST /v1/tasks/{tid}/comments |
Izoh qo'shish | 201 Created + Location |
tasks:write |
GET /v1/tasks/{tid}/comments |
Izohlar ro'yxati | 200 OK |
tasks:read |
POST /v1/projects/{pid}/members |
A'zo qo'shish | 201 Created |
members:write |
GET /v1/me |
Joriy foydalanuvchi | 200 OK |
profile:read |
E'tibor bering:
- Holat o'zgartirish uchun alohida
POST /tasks/{id}/donekabi fe'l-endpoint yaratmadik β buning o'rnigaPATCH /tasks/{id}bilan{"status": "done"}yuboramiz. Holat oddiy maydon bo'lsa, bu yetarli va resurs-orientatsiyani saqlaydi.
Trade-off β amal (action) endpointlari: Agar amal yon ta'sirli bo'lsa (masalan "vazifani arxivlash + xabarnoma yuborish + audit yozish"), uni oddiy maydon o'zgarishi emas, alohida sub-resurs sifatida ifodalash mantiqiyroq:
POST /v1/tasks/{id}/archive. Bizning misolda holat oddiy, shuning uchunPATCHyetarli. Qoida: "fe'l-endpoint" anti-naqshidan qoching, lekin murakkab amal sof CRUD'ga sig'masa, undan qo'rqmang (27-bob).
DELETEβ204 No Content(qaytaradigan tana yo'q). Ba'zilar200+ o'chirilgan ob'ekt qaytaradi β ikkalasi ham to'g'ri, lekin biz204ni izchillik uchun tanladik.POSTyaratishda har doimLocationsarlavhasi yangi resurs URL'ini beradi β mijoz uni qayta-yuklamasdan ham biladi.
Qadam 4 β So'rov va javob dizayni¶
Endpointlar ko'rinishi tayyor. Endi payloadlarni loyihalaymiz (7-bob: so'rov/javob payload).
Konvensiya qarorlari (bir marta tanlab, hamma joyda saqlaymiz):
- Maydon nomlari =
snake_case(due_date,assignee_id). - Vaqt = ISO 8601 / RFC 3339 string (
"2026-06-16T09:00:00Z"). - ID = string (kelajakda UUID'ga o'tishni osonlashtiradi; raqamga bog'lanib qolmaymiz).
- Pul yoki nozik sonlar yo'q bu domenda, lekin bo'lsa β string yoki integer-tiyin.
Vazifa yaratish β to'liq so'rov/javob:
POST /v1/projects/p_42/tasks HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 1d8e0f2a-7c3b-4a9e-b1c2-5f6a7b8c9d0e
{
"title": "Login sahifasini yangilash",
"assignee_id": "u_7",
"due_date": "2026-06-20T17:00:00Z",
"priority": "high"
}
HTTP/1.1 201 Created
Location: /v1/tasks/t_1001
Content-Type: application/json
{
"id": "t_1001",
"project_id": "p_42",
"title": "Login sahifasini yangilash",
"status": "todo",
"assignee_id": "u_7",
"due_date": "2026-06-20T17:00:00Z",
"priority": "high",
"created_at": "2026-06-16T09:12:00Z",
"updated_at": "2026-06-16T09:12:00Z"
}
E'tibor bering: javob faqat mijoz yuborganini qaytarmaydi β server tomonidan hosil qilingan maydonlarni ham (id, status: "todo" standart qiymat, created_at) qo'shadi. Mijoz hech qachon id yoki created_at yubormaydi; ularni faqat server boshqaradi.
Vazifalar ro'yxati β sahifalash va filtrlash (8-bob: sahifalash/filtrlash):
GET /v1/projects/p_42/tasks?status=todo&assignee_id=u_7&limit=20&cursor=eyJpZCI6InRfOTgwIn0 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{"id": "t_981", "title": "API hujjatini yozish", "status": "todo", "assignee_id": "u_7"},
{"id": "t_982", "title": "Testlarni qo'shish", "status": "todo", "assignee_id": "u_7"}
],
"pagination": {
"next_cursor": "eyJpZCI6InRfOTgyIn0",
"has_more": true
}
}
Trade-off β cursor vs offset: Biz cursor-asosli sahifalashni tanladik (
?cursor=...),?page=3&per_page=20(offset) emas. Sabab: vazifalar tez-tez qo'shiladi va o'chiriladi β offset bilan "3-sahifa" har safar siljiydi, foydalanuvchi bir elementni ikki marta ko'radi yoki o'tkazib yuboradi. Cursor barqaror. Narxi: foydalanuvchi to'g'ridan-to'g'ri "57-sahifa"ga sakray olmaydi β lekin task ro'yxatida bu kerak emas. Agar API admin-panel uchun bo'lsa va "umumiy soni + ixtiyoriy sahifaga sakrash" kerak bo'lsa, offset afzalroq bo'lardi.
Qadam 5 β Xato dizayni¶
Muvaffaqiyatli yo'lni chizdik. Endi xato yo'lini β bu ko'pincha e'tibordan chetda qoladigan, lekin DX (developer experience) uchun hal qiluvchi qism (9-bob: xato dizayni).
Standart format = RFC 9457 "Problem Details for HTTP APIs", media type application/problem+json. Hamma xato shu shaklda qaytadi β mijoz bitta parser yozadi.
Validatsiya xatosi (mijoz noto'g'ri ma'lumot yubordi):
POST /v1/projects/p_42/tasks HTTP/1.1
Content-Type: application/json
Authorization: Bearer <token>
{"title": "", "due_date": "kecha"}
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/validation-error",
"title": "So'rov ma'lumotlari yaroqsiz",
"status": 422,
"detail": "Ba'zi maydonlar talab darajasiga javob bermadi.",
"instance": "/v1/projects/p_42/tasks",
"errors": [
{"field": "title", "code": "required", "message": "Sarlavha bo'sh bo'lmasligi kerak"},
{"field": "due_date", "code": "invalid_format", "message": "ISO 8601 sana kutilgan"}
]
}
errors β bu kengaytma a'zosi (RFC 9457 standart a'zolardan tashqari qo'shimcha maydonlarga ruxsat beradi). U formaga mos validatsiya xatolarini bitta javobda beradi, shunda mijoz hamma maydon xatosini birdan ko'rsata oladi.
Status moslik jadvali (har holat β to'g'ri kod):
| Vaziyat | Status | Izoh |
|---|---|---|
| Forma yaroqsiz (semantik) | 422 Unprocessable Content |
JSON to'g'ri, lekin qiymatlar yaroqsiz |
| JSON sintaksisi buzuq | 400 Bad Request |
umuman parse bo'lmadi |
| Token yo'q / yaroqsiz | 401 Unauthorized |
autentifikatsiya yo'q |
| Token bor, lekin ruxsat yo'q | 403 Forbidden |
autentifikatsiya bor, avtorizatsiya yo'q |
| Resurs topilmadi | 404 Not Found |
yoki ko'rishga haqqi yo'q (BOLA yashirish) |
| Holat ziddiyati | 409 Conflict |
masalan allaqachon o'chirilgan |
| Rate limit | 429 Too Many Requests |
+ Retry-After |
Diqqat β 401 vs 403:
401= "kim ekanligingni bilmayman" (autentifikatsiya).403= "kim ekanligingni bilaman, lekin bunga haqqing yo'q" (avtorizatsiya). Bu farqni chalkashtirish β eng ko'p uchraydigan API xatosi. RFC 9110 buni aniq ajratadi.
Qadam 6 β Auth: kim va nimaga haqli¶
Endi qulfni o'rnatamiz. Ikki bosqich: autentifikatsiya (kim?) (11-bob) va avtorizatsiya (nimaga haqli?) (12-bob).
Autentifikatsiya tanlovi:
- Foydalanuvchi nomidan ishlovchi web/mobil mijozlar β OAuth 2.0 Authorization Code + PKCE. Bu β foydalanuvchiga yo'naltirilgan ilovalar uchun standart yo'l. (OAuth 2.1 yo'nalishi β hali draft β PKCE'ni hamma mijoz uchun majburiy qiladi va implicit/password grantlarni olib tashlaydi; biz shu yo'nalishga amal qilamiz.)
- Tashqi tizimlar (servis nomidan) β Client Credentials.
- Token formati: kirib kelgan token OAuth bearer token (RFC 6750), serverda esa biz uni JWT sifatida ifodalashimiz mumkin (
sub,scope,expclaimlar) β JWT bu token formati, OAuth bu protokol; ular bir-birini almashtirmaydi.
Scope (ruxsat doirasi) β token nimaga ruxsat berishini chegaralaydi. Endpoint jadvalida ko'rgan scope'lar: projects:read, projects:write, tasks:read, tasks:write, members:write, profile:read. Mobil ilova hamma scope'ni so'raydi; tashqi "faqat o'qish" integratsiyasi faqat *:read oladi.
Eng muhim qism β BOLA himoyasi (OWASP API1, 13-bob): scope "tasks:read" berilgani β foydalanuvchi istalgan vazifani o'qiy oladi degani emas. Har bir so'rovda server quyidagini tekshiradi:
Token egasi (
sub) shu vazifa tegishli loyihaning a'zosimi?
Agar yo'q bo'lsa β 404 Not Found (yo'qligini yashirib, 403 o'rniga β chunki 403 "bu resurs mavjud, lekin senga emas" deb ma'lumot sizdiradi). Bu tekshiruv har bir ob'ekt-darajali so'rovda bo'lishi shart; scope tekshiruvi uni almashtirmaydi.
GET /v1/tasks/t_1001 HTTP/1.1
Authorization: Bearer <boshqa-loyiha-a'zosining-tokeni>
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/not-found",
"title": "Vazifa topilmadi",
"status": 404,
"detail": "So'ralgan vazifa mavjud emas yoki sizga ko'rinmaydi.",
"instance": "/v1/tasks/t_1001"
}
Xavfsizlik: BOLA β OWASP API Security Top 10 (2023) ning birinchi zaifligi, chunki u eng keng tarqalgan va eng zararli. "ID URL'da bor, demak men uni so'rab tekshiraman" yetarli emas β har gal egalik (ownership) tekshirilishi kerak. Bu xato butun talablar ro'yxatidagi eng muhim NFRni buzadi.
Qadam 7 β Sifat: rate limit, idempotentlik, kesh¶
API ishlaydi va xavfsiz. Endi uni mustahkam qilamiz.
Rate limiting (14-bob) β bir mijoz tizimni bosib qo'ymasligi uchun (OWASP API4: Unrestricted Resource Consumption). Token bucket algoritmi, har token uchun limit. Limitga yetganda:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 1000
RateLimit-Remaining: 0
RateLimit-Reset: 30
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/rate-limited",
"title": "So'rovlar chegarasi oshib ketdi",
"status": 429,
"detail": "Iltimos, 30 sekunddan keyin qayta urinib ko'ring."
}
Retry-After standart (RFC 9110); RateLimit-* sarlavhalari IETF draft β biz ularni amaldagi konvensiya sifatida ishlatamiz va mijozga "qachon qaytish"ni aytamiz.
Idempotentlik (15-bob) β mobil mijoz vazifa yaratish so'rovini yuboradi, javob kelmaydi (tarmoq uzildi), mijoz qayta yuboradi. Ikkita vazifa yaralmasligi kerak. Yechim: POST so'rovlarda Idempotency-Key sarlavhasi (yuqoridagi yaratish misolida ko'rgan). Server kalitni saqlaydi: birinchi marta β ishlaydi va natijani keshlaydi; ikkinchi marta xuddi shu kalit bilan β kodni qayta ishlatmasdan saqlangan natijani qaytaradi.
Eslatma:
Idempotency-Keysarlavhasi IETF draft holatida (Stripe va boshqalar keng ishlatadi).GET,PUT,DELETEtabiatan idempotent (RFC 9110), shuning uchun kalit faqatPOSTuchun kerak.
Keshlash (16-bob, RFC 9111) β kam o'zgaradigan resurslarni keshlaymiz. Loyiha o'qishida ETag qaytaramiz; mijoz keyingi so'rovda If-None-Match bilan keladi; o'zgarmagan bo'lsa β 304 Not Modified (tana yo'q, tarmoq tejaladi). Bu ayni paytda optimistik bir-vaqtlilik uchun ham ishlaydi: PATCH da If-Match bilan kelgan eski ETag β 412 Precondition Failed (kimdir orada o'zgartirgan).
Qadam 8 β Versiyalash strategiyasi¶
API o'sadi. Bir kun keladi, payload shaklini sindiruvchi (breaking) o'zgarish kerak bo'ladi. Eski mijozlarni qanday saqlaymiz? (10-bob: versiyalash)
Biz URI versiyalashni tanladik β shuning uchun hamma yo'lda /v1/ prefiksi bor (/v1/projects, /v1/tasks).
Trade-off β URI vs header versiyalash: URI versiyalash (
/v1/...) brauzerda ko'rinadi, log'da o'qiladi,curlbilan sinash oson, keshlash bilan toza ishlaydi. Kamchiligi: "sof REST" nuqtai nazaridan bir resurs ikki URL'ga ega bo'ladi. Muqobil βAccept: application/vnd.example.v1+json(media type versiyalash) β sofroq, lekin ko'rinmas va sinash qiyin. Public API va keng auditoriya uchun URI versiyalash soddaligini afzal ko'rdik. Ichki mikroservislar uchun header versiyalash mosroq bo'lishi mumkin.
Qoidalar:
- Yangi ixtiyoriy maydon qo'shish β sindiruvchi emas; versiya o'zgartirmaymiz (additive).
- Maydonni o'chirish, qayta nomlash, majburiy qilish β sindiruvchi;
/v2/talab qiladi. /v1/ni o'chirishdan oldinDeprecation(RFC 9745) vaSunset(RFC 8594) sarlavhalari bilan ogohlantiramiz va hujjatda muddatni e'lon qilamiz.
Qadam 9 β OpenAPI: yagona haqiqat manbai¶
Endi shartnomani mashina o'qiy oladigan spetsifikatsiyaga aylantiramiz (21-bob: OpenAPI). OpenAPI spec β bu "yagona haqiqat manbai (source of truth)": undan hujjat, mijoz SDK, server stub va contract testlar avtomatik chiqadi.
Bir-ikki endpoint uchun OpenAPI 3.1 parchasi:
openapi: 3.1.0
info:
title: Task Manager API
version: 1.0.0
servers:
- url: https://api.example.com/v1
paths:
/projects/{pid}/tasks:
post:
summary: Loyihada yangi vazifa yaratish
operationId: createTask
security:
- oauth2: [tasks:write]
parameters:
- name: pid
in: path
required: true
schema:
type: string
- name: Idempotency-Key
in: header
required: false
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TaskCreate'
responses:
'201':
description: Vazifa yaratildi
headers:
Location:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Task'
'422':
description: Validatsiya xatosi
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
components:
securitySchemes:
oauth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/authorize
tokenUrl: https://auth.example.com/token
scopes:
tasks:read: Vazifalarni o'qish
tasks:write: Vazifalarni o'zgartirish
schemas:
TaskCreate:
type: object
required: [title]
properties:
title:
type: string
minLength: 1
assignee_id:
type: string
due_date:
type: string
format: date-time
priority:
type: string
enum: [low, medium, high]
Task:
type: object
required: [id, project_id, title, status, created_at]
properties:
id:
type: string
project_id:
type: string
title:
type: string
status:
type: string
enum: [todo, doing, done]
assignee_id:
type: [string, "null"]
due_date:
type: [string, "null"]
format: date-time
priority:
type: string
enum: [low, medium, high]
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
Problem:
type: object
properties:
type:
type: string
title:
type: string
status:
type: integer
detail:
type: string
instance:
type: string
E'tibor bering: assignee_id va due_date ning type: [string, "null"] shakli β JSON Schema 2020-12 (OpenAPI 3.1 unga moslangan) usuli "string yoki null" ifodalashning. 3.0 dagi nullable: true o'rniga shu ishlatiladi.
Standart: OpenAPI 3.1 (Feb 2021) JSON Schema 2020-12 bilan tekislangan; eng yangisi 3.2.0 (Sept 2025). Biz 3.1 sintaksisida yozdik β u keng qo'llab-quvvatlanadi.
Qadam 10 β Hujjat, test, observability¶
Spec tayyor. Oxirgi qadam β uni ishonchli va qulay qilish.
Hujjat va DX (22-bob: hujjatlash/DX): OpenAPI'dan interaktiv hujjat (Swagger UI / Redoc) avtomatik chiqadi. Lekin yaxshi DX undan ko'proq: "Getting Started" yo'riqnomasi, real curl namunalar, autentifikatsiya bo'yicha qadam-baqadam, xatolar lug'ati. Hujjat β API'ning yuzi; ko'p dasturchi avval hujjatni ko'radi, keyin qaror qiladi.
Test (24-bob: testlash/contract): ikki qatlam.
- Contract testlar β implementatsiya OpenAPI spec'ga mos kelishini tekshiradi (spec aytgan shakl = server qaytargan shakl). Bu β spec'ning "yolg'on gapirmasligi"ning kafolati.
- Integratsiya testlar β har endpoint, ayniqsa xavfsizlik yo'llari: "boshqa loyiha a'zosi
t_1001ni o'qiy olmaydi" testini albatta yozamiz (BOLA regressiyasini ushlash uchun).
Observability (25-bob: observability): har so'rovda structured log (kim, qaysi endpoint, status, davomiyligi), trace_id (so'rovni xizmatlar bo'ylab kuzatish), metrikalar (so'rov soni, xato foizi, p95 kechikish). Muammo chiqsa, "nima bo'ldi"ni log'dan emas, balki dashboarddan ko'rasiz.
To'liq ishlangan misol: yana bir bor, qisqacha¶
Yuqorida har qadamni alohida ko'rdik. Mana ularning hammasi bitta vazifa-boshqaruvchi API'ning yaxlit shartnomasi sifatida:
- Talab: multi-tenant task manager; web/mobil/tashqi mijozlar; eng muhim NFR β har kim faqat o'z loyihalarini ko'radi.
- Resurslar:
users,projects,tasks,comments,membersβ uyalash bilan, lekin elementga tekis murojaat. - Endpointlar: CRUD + filtr + sahifalash; metod/status RFC 9110'ga mos; yaratishda
201+Location. - Payload:
snake_case, RFC 3339 vaqt, server-hosil maydonlar; cursor sahifalash. - Xato: RFC 9457 Problem Details,
errorskengaytmasi bilan validatsiya. - Auth: OAuth Code+PKCE (foydalanuvchi), Client Credentials (servis); scope + har so'rovda BOLA tekshiruvi.
- Sifat: 429 +
Retry-After;Idempotency-KeyPOST uchun;ETag/If-None-Matchkesh vaIf-Matchbir-vaqtlilik. - Versiya: URI
/v1/; additive o'sish;Deprecation/Sunsetbilan iste'foga chiqarish. - Spec: OpenAPI 3.1 β yagona haqiqat manbai.
- Sifat-kafolat: hujjat, contract + xavfsizlik testlari, structured log + trace + metrikalar.
Asosiy trade-off qarorlari va ularning sababi:
| Qaror | Tanlov | Sabab |
|---|---|---|
| Sahifalash | Cursor | Ro'yxat tez o'zgaradi β offset siljiydi |
| Versiyalash | URI /v1/ |
Ko'rinarli, sinash oson, keng auditoriya |
| Holat o'zgartirish | PATCH maydon |
Oddiy holat β fe'l-endpoint kerak emas |
| 403 o'rniga 404 | Yashirish | Resurs mavjudligini sizdirmaslik (BOLA) |
| ID formati | String | UUID'ga oson o'tish; DB internalini sizdirmaslik |
Dizayn review: o'zini tekshirish¶
Loyihalash tugagach, 27-bob ruhidagi checklist bilan o'zingizni tekshiring. Har bir savolga "ha" deya olsangiz, dizayn mustahkam:
- [ ] URL'lar otlardanmi (resurs), fe'llardan emasmi? (
/tasks,/getTasksemas) - [ ] Har metod RFC 9110 semantikasiga mosmi? (
GETsafe,DELETEidempotent) - [ ] Har status kod to'g'rimi? (yaratish
201, o'chirish204,401vs403) - [ ] Xato formati izchilmi (RFC 9457), hamma joyda bir xilmi?
- [ ] BOLA β har ob'ekt-so'rovda egalik tekshirilyaptimi?
- [ ] Yaratish so'rovlari idempotentmi (
Idempotency-Key)? - [ ] Payload maydon konvensiyasi (
snake_case) hamma joyda bir xilmi? - [ ] Sahifalash strategiyasi tanlangan va izchilmi?
- [ ] Versiyalash rejasi bormi; sindiruvchi o'zgarish qanday boshqariladi?
- [ ] OpenAPI spec server bilan mosmi (contract test)?
- [ ] Server internallari (DB ustun nomlari, stack-trace) sizib chiqmayaptimi?
Anti-pattern: Eng ko'p uchraydigan to'rtta: (1) fe'l-URL (
/createTask); (2) hamma xatoga200 OK+ tanada{"error": ...}; (3) egalik tekshiruvisiz ID (BOLA); (4) izchilsiz maydon nomlari (due_datebir joyda,dueDateboshqasida). Ularning hammasidan yuqoridagi jarayon himoya qiladi.
Keyingi qadamlar¶
Loyihalashni bilish β yarmi. Endi eng muhimi β amaliyot.
- Real API quring. Bu dizaynni tanlagan tilingizda implementatsiya qiling β Node.js, Laravel yoki Django bilan. Spec'dan boshlang, keyin kod. Birinchi loyihangiz mukammal bo'lmaydi β bu normal.
- Tizim darajasiga ko'taring. API bitta xizmat ichidagi shartnoma; uni katta tizim ichida joylash (xizmatlar chegarasi, gateway, ma'lumotlar oqimi) β alohida fan. Dasturiy arxitektura kitobi shu haqida.
- Mashhur API'larni o'rganing. Stripe (xato dizayni va idempotentlik etaloni), GitHub (resurs modeli va pagination), Twilio (DX). Yaxshi API'ni o'qish β yaxshi yozishning eng tez yo'li.
- OpenAPI'ni avtomatlashtiring. Spec'dan SDK, hujjat va contract test chiqarishni CI'ga ulang β shunda shartnoma har doim haqiqatga mos qoladi.
Yakuniy so'z¶
Butun kitob bo'ylab bir g'oya qaytarildi: API β bu shartnoma. U mashina o'qiy oladigan formatda yozilgan, lekin uni o'qiydiganlar β odamlar, sizdan keyingi dasturchilar. Yaxshi API ikki narsadan iborat:
- Izchillik β bir marta o'rgangan qoida hamma joyda ishlaydi. Status kodlar, maydon nomlari, xato shakli, sahifalash β bashorat qilinadigan. Izchillik ishonch hosil qiladi.
- Empatiya β siz API'ni o'zingiz uchun emas, uni ishlatadigan dasturchi uchun loyihalaysiz. Xato xabari unga yo'l ko'rsatadimi? Hujjat birinchi 5 daqiqada ishlatib ko'rishga yetadimi? Bu β texnik emas, insoniy savol.
Bularning ikkalasi ham bir kechada kelmaydi. Ular mashq bilan β har bir loyiha, har bir review, har bir "nega bu xato shunday qaytdi?" savoli bilan keladi. Siz endi vositalarni ham, jarayonni ham bilasiz. Qolgan β qurish. Omad.
Asosiy g'oyalar (bobni qisqacha)¶
- API dizayni jarayon β talabdan boshlanadi, spec'da yakunlanadi, kod eng oxirida. Klaviaturadan emas, savoldan boshlang.
- O'n qadam: talab β resurs β endpoint β payload β xato β auth β sifat β versiya β OpenAPI β hujjat/test/observability. Har qadam oldingi bobdagi vositadan foydalanadi.
- Har qaror β trade-off. Cursor vs offset, URI vs header versiya, PATCH vs amal-endpoint β kontekst hal qiladi, mutlaq qoida emas. Tanlovni asoslay olish β tanlovning o'zidan muhimroq.
- Xavfsizlik tasodif emas β BOLA himoyasi (har ob'ekt-so'rovda egalik tekshiruvi) dizaynning birinchi sinfli qismi; OWASP API1.
- Izchillik + empatiya = yaxshi API. Standart (RFC 9110/9457/9111, OpenAPI) izchillikni beradi; dasturchiga g'amxo'rlik empatiyani. Qolgani β mashq.
Mashqlar¶
Oson¶
1-mashq. Quyidagi talab berilgan: "Foydalanuvchilar kitoblarni ro'yxatga oladi va har bir kitobga sharh (review) yozadi." Bu domendan ikkita resurs va ular orasidagi bog'lanishni aniqlang.
2-mashq. Loyihalash jarayonining quyidagi qadamlarini to'g'ri tartibda joylashtiring: OpenAPI yozish Β· resurslarni aniqlash Β· talablarni to'plash Β· endpointlarni belgilash Β· xato formatini dizayn qilish.
3-mashq. "Yangi kitob qo'shish" amali uchun to'g'ri HTTP metod, URL va muvaffaqiyat status kodini ayting.
O'rta¶
4-mashq. 1-mashqdagi kitob/sharh domeni uchun endpoint jadvali tuzing (kamida 5 endpoint): metod, URL, status. Sharh kitobga uyalangan bo'lsin.
5-mashq. Shu API uchun auth dizayni chizing: qaysi grant (foydalanuvchi mijozi uchun), qanday scopelar kerak, va "boshqa odamning sharhini o'chirish"ni qanday to'sasiz (qaysi tekshiruv, qaysi status)?
6-mashq. "Kitobga yangi sharh qo'shish" uchun to'liq so'rov va javob blokini yozing (HTTP metod, yo'l, sarlavhalar, valid JSON tana; server 201 qaytarsin, javob server-hosil maydonlarni o'z ichiga olsin).
Qiyin¶
7-mashq. Butunlay boshqa domen β stol bron qilish (restoran reservation) tizimi β uchun API'ni noldan loyihalang: (a) 3 ta talab/NFR, (b) resurs xaritasi, (c) kamida 6 endpointli jadval (auth scope bilan), (d) bitta endpointning to'liq so'rov/javob misoli.
8-mashq. Shu bron tizimi uchun ikkita dizayn qarorini trade-off bilan asoslang: (1) sahifalash cursor yoki offset; (2) versiyalash URI yoki header. Har biri uchun "qachon teskari tanlov to'g'ri bo'lardi"ni ham ayting.
9-mashq. Bron yaratish endpointi uchun OpenAPI 3.1 parchasi yozing (paths ostida bitta POST operatsiya: requestBody sxemasi, 201 va 409 javoblar). 409 β bir vaqtda ikki kishi bir stolni bron qilishga urinsa.
Yechimlar
1-mashq yechimi¶
Resurslar: books (kitob) va reviews (sharh). Bog'lanish: bir kitobga ko'p sharh (one-to-many). Sharh kitobsiz mavjud bo'lmaydi, shuning uchun u kitobga tegishli (uyalangan) sub-resurs. Foydalanuvchi ham resurs bo'lishi mumkin (users), lekin talab "ikkita resurs" so'radi, asosiylari β books va reviews.
2-mashq yechimi¶
To'g'ri tartib:
- Talablarni to'plash
- Resurslarni aniqlash
- Endpointlarni belgilash
- Xato formatini dizayn qilish
- OpenAPI yozish
Mantiq: nima qilishimizni (talab) bilmasak, resurslarni ajrata olmaymiz; resurslarsiz endpoint yo'q; xato formati endpointlar shaklini bilgach aniqlanadi; OpenAPI β hamma qaror tayyor bo'lgach yoziladigan yakuniy shartnoma.
3-mashq yechimi¶
- Metod:
POST(yangi element yaratish β safe emas, idempotent emas). - URL:
POST /v1/books(kolleksiyaga qo'shish). - Status:
201 Created, javobdaLocation: /v1/books/{yangi_id}sarlavhasi bilan.
4-mashq yechimi¶
| Metod va URL | Maqsad | Status |
|---|---|---|
POST /v1/books |
Kitob qo'shish | 201 Created + Location |
GET /v1/books |
Kitoblar ro'yxati (sahifa) | 200 OK |
GET /v1/books/{bid} |
Bitta kitob | 200 OK |
POST /v1/books/{bid}/reviews |
Sharh qo'shish | 201 Created + Location |
GET /v1/books/{bid}/reviews |
Kitob sharhlari ro'yxati | 200 OK |
GET /v1/reviews/{rid} |
Bitta sharh (tekis murojaat) | 200 OK |
DELETE /v1/reviews/{rid} |
Sharhni o'chirish | 204 No Content |
Ro'yxat/yaratish kitobga uyalangan (/books/{bid}/reviews), bitta sharhga murojaat tekis (/reviews/{rid}) β chuqur uyalashdan qochish uchun.
5-mashq yechimi¶
- Grant: foydalanuvchi nomidan ishlovchi web/mobil mijoz uchun OAuth 2.0 Authorization Code + PKCE (OAuth 2.1 yo'nalishi β PKCE majburiy, implicit/password yo'q).
- Scope'lar:
books:read,reviews:read,reviews:write. (Kitob qo'shish admin amali bo'lsa β alohidabooks:write.) - "Boshqa odamning sharhini o'chirish"ni to'sish:
reviews:writescope yetarli emas β bu BOLA (OWASP API1). ServerDELETE /v1/reviews/{rid}da token egasi (sub) shu sharh muallifimi (yoki admin) ekanini tekshiradi. Agar yo'q bo'lsa β404 Not Found(resurs mavjudligini sizdirmaslik uchun403o'rniga 404). Scope "nima turdagi amal"ni, egalik tekshiruvi "qaysi aniq ob'ektga" ruxsatni boshqaradi β ikkalasi ham kerak.
6-mashq yechimi¶
POST /v1/books/b_15/reviews HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
{
"rating": 5,
"text": "Ajoyib kitob, API dizaynini noldan o'rgatadi."
}
HTTP/1.1 201 Created
Location: /v1/reviews/r_900
Content-Type: application/json
{
"id": "r_900",
"book_id": "b_15",
"author_id": "u_3",
"rating": 5,
"text": "Ajoyib kitob, API dizaynini noldan o'rgatadi.",
"created_at": "2026-06-16T10:30:00Z"
}
Server id, book_id (yo'ldan), author_id (tokendan), created_at ni o'zi hosil qildi β mijoz ularni yubormaydi.
7-mashq yechimi¶
(a) Talab/NFR:
- Foydalanuvchilar restoranlarni ko'radi va ma'lum sana/vaqtga stol bron qiladi.
- NFR β bir-vaqtlilik: ikki kishi bir stolni bir vaqtga bron qila olmasligi kerak (ortiqcha-bron yo'q).
- NFR β xavfsizlik: foydalanuvchi faqat o'z bronlarini ko'radi/bekor qiladi (BOLA himoyasi).
(b) Resurs xaritasi:
restaurants(ildiz) β ko'ptables(stol).restaurantsβ ko'preservations(bron); brontable_id,user_id,start_time,party_sizega ega.usersβ ko'preservations.
(c) Endpoint jadvali:
| Metod va URL | Maqsad | Status | Scope |
|---|---|---|---|
GET /v1/restaurants |
Restoranlar ro'yxati | 200 OK |
restaurants:read |
GET /v1/restaurants/{rid}/availability?date=... |
Bo'sh stollar | 200 OK |
restaurants:read |
POST /v1/restaurants/{rid}/reservations |
Bron yaratish | 201 Created + Location |
reservations:write |
GET /v1/reservations/{resid} |
Bitta bron | 200 OK |
reservations:read |
DELETE /v1/reservations/{resid} |
Bronni bekor qilish | 204 No Content |
reservations:write |
GET /v1/me/reservations |
Mening bronlarim | 200 OK |
reservations:read |
(d) To'liq so'rov/javob:
POST /v1/restaurants/rest_8/reservations HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 9a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9
{
"table_id": "tbl_3",
"start_time": "2026-06-20T19:00:00Z",
"party_size": 4
}
HTTP/1.1 201 Created
Location: /v1/reservations/res_555
Content-Type: application/json
{
"id": "res_555",
"restaurant_id": "rest_8",
"table_id": "tbl_3",
"user_id": "u_42",
"start_time": "2026-06-20T19:00:00Z",
"party_size": 4,
"status": "confirmed",
"created_at": "2026-06-16T11:00:00Z"
}
8-mashq yechimi¶
(1) Sahifalash β bu yerda offset to'g'riroq. Restoranlar ro'yxati nisbatan barqaror (kunlik tez-tez qo'shilib-o'chmaydi) va foydalanuvchi "umumiy soni"ni ko'rishi, "5-sahifa"ga sakrashi qulay bo'lishi mumkin β offset shuni beradi. Teskari tanlov qachon to'g'ri: agar biz juda tez o'sadigan oqimni (masalan real-vaqt bron-loglari) sahifalasak, offset siljiydi va dublikat/o'tkazib-yuborish chiqadi β o'shanda cursor afzal.
(2) Versiyalash β URI (/v1/). Bu public-ga moyil iste'mol API; mijozlar turli (web/mobil/uchinchi-tomon), URL ko'rinadigan va sinash oson bo'lsin. Teskari tanlov qachon to'g'ri: agar bu faqat ichki mikroservislar orasidagi shartnoma bo'lsa va biz URL'ni "toza resurs identifikatori" sifatida saqlamoqchi bo'lsak, Accept media-type versiyalash sofroq bo'lardi.
9-mashq yechimi¶
openapi: 3.1.0
info:
title: Restaurant Reservation API
version: 1.0.0
paths:
/restaurants/{rid}/reservations:
post:
summary: Restoranda stol bron qilish
operationId: createReservation
security:
- oauth2: [reservations:write]
parameters:
- name: rid
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [table_id, start_time, party_size]
properties:
table_id:
type: string
start_time:
type: string
format: date-time
party_size:
type: integer
minimum: 1
responses:
'201':
description: Bron yaratildi
headers:
Location:
schema:
type: string
content:
application/json:
schema:
type: object
properties:
id:
type: string
status:
type: string
'409':
description: Stol shu vaqtga allaqachon band
content:
application/problem+json:
schema:
type: object
properties:
type:
type: string
title:
type: string
status:
type: integer
detail:
type: string
409 Conflict β ortiqcha-bron holatining to'g'ri kodi: so'rov o'zi yaroqli (422 emas), lekin resursning joriy holati bilan ziddiyatda. Bron yozuvini saqlashda (table_id, start_time) ustida unikal cheklov yoki optimistik qulflash bilan poyga-holatidan himoyalanamiz.
β¬ οΈ Oldingi: 27 β Dizayn naqshlari va anti-naqshlari Β· π README