REST API Tasarlarken Dikkat Edilmesi Gerekenler

Kaynak modeli ve HTTP semantiğinden pagination, idempotency ve eşzamanlı güncellemelere kadar uzun ömürlü bir API sözleşmesi tasarlayalım.

June 23, 2026•4 min read•

Bir endpoint bugün frontend'in ihtiyacını karşılayabilir. Fakat mobil istemci, entegrasyon ortağı veya eski uygulama sürümü devreye girdiğinde API artık bir fonksiyon çağrısından fazlasıdır: farklı tarafların güvendiği bir sözleşmedir.

İyi tasarım, URL'lerin güzel görünmesiyle sınırlı değildir. Hataların anlamı, tekrar gönderilen istekler, veri sınırları ve geriye uyumluluk da bu sözleşmeye dahildir.

Kaynakları iş modeline göre belirleyin

Sipariş sisteminde /orders koleksiyonu ve /orders/{id} kaynağı anlaşılır bir başlangıçtır. İstemcinin görmek zorunda olmadığı iç servis veya tablo isimlerini URL'ye taşımak gereksiz bağımlılık yaratabilir.

GET /api/orders?status=pending&limit=20
POST /api/orders
GET /api/orders/order_42
PATCH /api/orders/order_42

Her iş eylemini yapay biçimde CRUD içine sıkıştırmanız gerekmez. Sipariş iptali ayrı kuralları olan bir işlemse, iptal kaynağı veya açık bir eylem modeli tasarlayabilirsiniz. Önemli olan yöntemin tutarlı ve belgelenmiş olmasıdır.

HTTP metotlarının anlamına saygı gösterin

GET, istemcinin durum değiştirme talebi olarak kullanılmamalıdır. PUT hedef kaynağın temsiline ilişkin değiştirme semantiği taşır; PATCH kısmi değişiklik için kullanılır. DELETE'in idempotent olması, aynı isteğin her seferinde aynı status code'u döndürmesi demek değildir. Tekrarlanan isteğin amaçlanan etkisinin değişmemesiyle ilgilidir.

POST genel olarak idempotent kabul edilmez. PATCH'in de her kullanımının idempotent olması garanti değildir: “sayacı bir artır” ile “durumu completed yap” farklıdır. Temel anlamlar HTTP Semantics standardında tanımlanır.

Bu semantiği doğru kullanmak; cache, retry ve proxy gibi aracıların davranışını daha öngörülebilir yapar.

Başarı ve hatayı ayırt edin

Oluşturulan kaynak için 201 ve uygun Location header'ı, başarıyla işlenen fakat içerik döndürmeyen işlem için 204 düşünülebilir. 202 ise işlemin kabul edildiğini, henüz tamamlanmadığını belirtir; ardından durum sorgulama yolu sunmak gerekir.

Hataları her zaman 200 içinde success:false olarak döndürmek istemcileri ve gözlem araçlarını zorlar. Benzer biçimde tüm hatalara 500 demek, kullanıcı girdisiyle sunucu arızasını birbirine karıştırır.

Tutarlı hata nesnesi için Problem Details yaklaşımından yararlanabilirsiniz:

{
  "type": "https://api.example.com/problems/invalid-order",
  "title": "Sipariş doğrulanamadı",
  "status": 422,
  "detail": "En az bir ürün seçilmelidir.",
  "instance": "/api/orders/requests/example-42"
}

Alan adları örnek sözleşmedir; type adresi gerçek uygulamada ilgili problem türünü açıklamalıdır. Yanıt türü application/problem+json olur. Ayrıntılar RFC 9457 içinde yer alır. Stack trace veya SQL sorgusu gibi iç bilgileri kullanıcı hatasına taşımayın.

Doğrulama ve yetkilendirmeyi ayırın

Doğru biçimli bir order ID, kullanıcının o siparişi görmeye yetkili olduğu anlamına gelmez. Her kaynak erişiminde tenant veya sahiplik sınırını değerlendirin. Yalnızca login kontrolü yapmak nesne düzeyindeki yetkilendirmeyi çözmez.

İstek gövdesini doğrudan veritabanı modeline kopyalamak da tehlikelidir. İstemciye açık alanları allowlist ile belirleyin. role, ownerId veya price gibi alanların hangi tarafça hesaplandığı açık olmalıdır.

Örneğin sipariş toplamını frontend gönderiyor diye güvenilir kabul etmeyin. Sunucu ürün fiyatı ve indirim kurallarına göre toplamı hesaplayabilir; istemci gönderimi yalnızca kullanıcı niyetini taşımalıdır.

Pagination ve veri hacmi

Küçük tabloda tüm kayıtları döndürmek sorunsuz görünür. Veri büyüyünce yanıt süresi, bellek ve ağ maliyeti artar. Limit için varsayılan ve üst sınır belirleyin.

Offset pagination basittir fakat hareketli veri üzerinde kayıt atlama veya tekrar gösterme davranışı yaratabilir. Cursor pagination, özellikle kararlı bir sıralama anahtarıyla daha uygun olabilir. Yalnızca createdAt yeterli değilse ID ile birlikte sıralayın.

Cursor'ın gizlenmiş olması yetkilendirme sağlamaz. Cursor çözüldükten sonra kullanıcı filtresi yine uygulanmalı, sıralama ve filtre sözleşmesi değişiklikleri düşünülmelidir.

Retry için idempotency tasarlayın

Ödeme veya sipariş oluşturma isteğinin cevabı kaybolabilir. İstemci tekrar gönderdiğinde ikinci kayıt oluşturmamak için bir idempotency anahtarı kullanılabilir.

Anahtarı kullanıcı veya tenant kapsamında saklayın; aynı anahtarın farklı body ile kullanımını reddedin. Eşzamanlı iki istekte yalnızca cache kontrolü yapmak yarış koşulunu engellemez. Atomik kayıt veya veritabanı unique constraint gibi bir mekanizma gerekir.

Anahtarın saklama süresi, işlemin ne kadar süre tekrar edilebileceğiyle uyumlu olmalıdır. Bu davranış generic HTTP'nin otomatik verdiği bir özellik değil, sizin uygulama sözleşmenizdir.

Aynı kaydı iki kişi güncellerse?

İki istemci eski temsil üzerinden güncelleme yaparsa biri diğerinin değişikliğini ezebilir. ETag ve If-Match gibi koşullu istekler veya sürüm alanıyla optimistic concurrency kontrolü kullanılabilir.

Uygulama önce sürümü okuyup sonra koşulsuz yazarsa arada başka değişiklik olabilir. Kontrol ile güncellemenin veri katmanında aynı atomik koşula bağlı olması gerekir. Uyuşmazlığın istemciye nasıl bildirileceği ve yeniden deneme akışı da belgelenmelidir.

API tasarımının son testi, yalnızca mutlu yol değildir: eksik yetki, tekrar, eşzamanlılık, büyük veri ve eski istemci koşullarında da anlamlı davranmalıdır.