Rules
Öne çıkan·v1.0.0·2026-04-22API Tasarım Kuralları
REST API tasarım kuralları — URL yapısı, HTTP method, status code, versioning, error format, pagination, idempotency, rate limit, dokümantasyon. CLAUDE.md / .cursorrules içeriği olarak kullanılır.
apiresthttpdesignrulesbackendclaude-md
İçerik
Bu içeriği projenin CLAUDE.md / AGENTS.md / .cursorrules dosyasına yapıştır.
REST API Tasarım Kuralları
URL yapısı
İsimlendirme
- Kaynak isimleri çoğul, küçük harf, kebab-case:
✅ GET /users ✅ GET /order-items ❌ GET /getUsers ❌ GET /orderItems ❌ GET /User - Action verbi URL'de yasak — HTTP method verbi taşır:
✅ POST /users (create) ✅ DELETE /users/123 (delete) ❌ POST /createUser ❌ POST /users/123/delete - İstisna: bir state transition CRUD'a uymuyorsa — sub-resource veya action:
POST /orders/123/cancel POST /users/123/password-reset - Path param = resource identifier:
GET /users/{user_id} GET /users/{user_id}/orders GET /users/{user_id}/orders/{order_id} - Nesting maksimum 2 seviye — aksi belirsizleşir. 3. seviye için flat:
❌ /users/1/projects/2/tasks/3/comments ✅ /tasks/3/comments (task globally unique ise)
HTTP method
| Method | Ne için | Idempotent? | Body? |
|---|---|---|---|
GET |
Listele / fetch | Evet | Hayır |
POST |
Oluştur / action | Hayır | Evet |
PUT |
Tam replace | Evet | Evet |
PATCH |
Kısmi güncelleme | Genelde evet | Evet |
DELETE |
Sil | Evet | Nadiren |
GEThiçbir yan etki yaratmaz. Cache'lenir, tekrar edilebilir.PUT= replace. Resource'u verdiğin body ile değiştir (full).PATCH= merge. Belirtilen alanları güncelle. JSON Merge Patch ya da JSON Patch.DELETEidempotent — ikinci kez çağırınca 404 değil, 204 dön (veya 404 + tutarlı).
Status code
2xx — Başarı
- 200 OK — başarılı, body var
- 201 Created — oluşturuldu,
Location: /resource/idheader - 202 Accepted — async, işlem başlatıldı (queue / background)
- 204 No Content — başarılı, body yok (DELETE sonrası)
3xx — Yönlendirme
- 301 Moved Permanently — URL değişti kalıcı
- 302 Found / 307 Temporary — geçici redirect
- 304 Not Modified — ETag / If-Modified-Since match
4xx — Client hatası
- 400 Bad Request — validation hatası, malformed
- 401 Unauthorized — authentication eksik
- 403 Forbidden — authenticated ama yetki yok
- 404 Not Found — resource yok
- 405 Method Not Allowed — resource var ama method desteklenmiyor
- 409 Conflict — state çakışması (duplicate, race)
- 410 Gone — eskiden vardı, şimdi silindi / kalıcı kapalı
- 422 Unprocessable Entity — syntax OK, semantic invalid (business rule)
- 429 Too Many Requests — rate limit
5xx — Server hatası
- 500 Internal Server Error — beklenmeyen hata
- 502 Bad Gateway — upstream hatası
- 503 Service Unavailable — geçici indir / maintenance
- 504 Gateway Timeout — upstream timeout
Kullanılmayan: 200 OK başarısızlık body'siyle — yasak. Status code doğru döner.
Error response formatı
Tek format, tüm endpoint'lerde:
{
"error": {
"code": "order_out_of_stock",
"message": "Requested quantity exceeds available stock",
"details": {
"product_id": "prod_123",
"requested": 10,
"available": 3
},
"trace_id": "req_abc123"
}
}
code— machine-readable, snake_case, stable (client switch-case)message— human-readable, developer için (localize etme, ayrı mekanizma)details— opsiyonel, structured contexttrace_id— log correlation
Validation hatalarında (400) her alan için:
{
"error": {
"code": "validation_failed",
"fields": {
"email": ["required", "must_be_valid_email"],
"age": ["must_be_positive"]
}
}
}
Versioning
- URL path'te major version:
/v1/,/v2/(sade, caching-friendly).GET /v1/users GET /v2/users - Breaking change → yeni major. Aynı v1 altında breaking değişiklik yasak.
- Non-breaking eklemeler (yeni alan, yeni endpoint) mevcut version'da.
- Deprecation: header ile (
Deprecation: true,Sunset: <tarih>) + dokümantasyon. - En az 6 ay paralel yaşam (v1 + v2), sonra eski emekli edilir.
Request / response conventions
Naming
- JSON field'lar
snake_case(dilden bağımsız, interop friendly):{ "user_id": "...", "created_at": "..." } - Timestamp ISO 8601 UTC:
2026-04-22T14:30:00Z. - ID'ler string (integer overflow, prefix'lenebilir):
{ "id": "usr_01HW3KJD..." } - Boolean alan adı:
is_active,has_verified_email, değilactive,verified. - Enum değerleri:
snake_casestring (tip güvenli ama esnek).
Pagination
Cursor-based (tercih):
GET /users?limit=50&cursor=eyJpZCI6InVzcl8xMjMifQ
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6InVzcl80NTYifQ",
"has_more": true
}
}
Offset-based sadece küçük, stable dataset'te kabul:
GET /users?page=2&per_page=50
- Default limit (20, 50, 100) + max limit (genelde 100-200).
limitveper_pageaynı anda kullanılmaz — projede biri seçilir.
Filter / sort / search
GET /orders?status=pending&created_after=2026-01-01&sort=-created_at
- Filter query param + eşitlik. Karmaşıklaşırsa:
?status=pending,paid(comma-separated). - Sort
-created_at= desc,created_at= asc. Multi-sort comma:sort=status,-created_at. - Range:
created_after,created_before(net).gte:,lte:prefix'i de tercih edilebilir. - Search:
?q=...full-text.
Idempotency
POST(create) için:Idempotency-Keyheader (UUID).- Server 24-48 saat key + response cache tutar. Aynı key ile tekrar → cached response (201 + aynı body).
- Her "hassas" POST için zorunlu yap (payment, refund, email send).
PUT,DELETE,GETdoğaları gereği idempotent — Idempotency-Key gereksiz.
Authentication & Authorization
- Bearer token
Authorization: Bearer <jwt|opaque>. - 401 = auth yok/invalid. 403 = auth OK ama yetki yok. Karıştırma.
- JWT expiration makul (< 1 saat), refresh token ayrı.
- API key sadece server-to-server. Client (mobile/web) = OAuth token.
- Scope-based authorization (
orders:read,orders:write) basit role'den daha esnek.
Rate limiting
- Response headers:
X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 899 X-RateLimit-Reset: 1700000000 - 429 Too Many Requests +
Retry-Afterheader. - Tier'lar: anonymous < authenticated < premium.
- Per-endpoint limit bazı endpoint'ler için (login, password reset) tighter.
CORS
- Cross-origin kullanıma açık endpoint'te:
Access-Control-Allow-Origin: https://app.example.com (specific) Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Authorization, Content-Type Access-Control-Max-Age: 86400 *ile allow-origin sadece tamamen public, auth-free API için.
Request body size
- Limit var (10MB gibi). Config'te tanımlı.
- Binary upload ayrı endpoint (multipart/form-data veya presigned URL to S3).
- Arbitrary JSON içinde binary yok — ya base64 (ayrı endpoint) ya presigned.
Dokümantasyon
- OpenAPI 3.0+ spec kodun yanında tutulur (inline veya ayrı YAML).
- Swagger UI / Redoc otomatik üretilir.
- Her endpoint için: açıklama, request/response schema, örnek, error code'ları.
- Changelog — her version bump'ta değişenler listelenir.
- SDK / client library otomatik üretilir (OpenAPI Generator).
Güvenlik minimumu
- HTTPS zorunlu. HTTP isteği → redirect ya da 403.
- Input validation schema-based, her endpoint.
- Output escape — XSS için, response'da kullanıcı content echo'lanıyorsa.
- SQL parametreli query — concat yasak.
- Secret log'a girmez — token, password, PII log'da görünmez.
- CSRF cookie-based session varsa (SameSite + token).
Async / Webhooks
Long-running operation
POST /reports/generate
→ 202 Accepted
Location: /reports/generate/status/{job_id}
GET /reports/generate/status/{job_id}
→ { "status": "pending" | "running" | "done" | "failed", "result_url": "..." }
Webhooks
- HMAC signature body + timestamp üzerinde.
- Retry: exponential backoff, at-least-once.
- Idempotency-Key webhook payload'da.
- Versioning: header ile (
X-Webhook-Version: 1).
Yasak listesi (özet)
- URL'de verb (
/getUser,/createOrder) - 200 OK +
{"success": false}body idinteger (stringified ID kullan)- Nested 3+ seviye resource
camelCaseJSON field (dilden dile çeviride sorun)- PATCH ≠ PUT ayrımı belirsiz bırakmak
- Response'da farklı error format'lar
- Breaking change aynı version'da
- API key URL query param'da (log'a sızar)
- CORS
*auth-required endpoint'te - Rate limit bilgisi yok (client tahmin edemez)
- Dokümantasyon kodla senkron değil
Örnek iyi endpoint
POST /v1/orders:
summary: Yeni sipariş oluştur
headers:
Idempotency-Key:
required: true
schema: { type: string, format: uuid }
Authorization:
required: true
request_body:
type: object
required: [items]
properties:
items:
type: array
items:
type: object
required: [product_id, qty]
properties:
product_id: { type: string }
qty: { type: integer, minimum: 1 }
responses:
201:
description: Sipariş oluşturuldu
headers:
Location: { schema: { type: string } }
body:
order_id: string
status: string
total: number
created_at: string (iso8601)
400: validation_failed
409: idempotency_replay (cached response)
422: insufficient_stock
Commit
Conventional Commits. Endpoint eklentisi feat(api): ..., breaking change feat(api)!: + BREAKING CHANGE footer.