Bloga dön

30 Mayıs 2026

Java ve Spring ile Sağlam REST API'ler: Sahadan Notlar

#java#spring#rest-api#backend

İlk ciddi REST API'lerimi Spring Web ile yazdım — dosya indirme/yükleme servisleri, veri temizleme araçlarının HTTP arayüzleri. Sonrasında kurumsal projelerde ve telekom entegrasyonlarında aynı temelin üzerine koydum. Bu yazı, o günden bugüne taşıdığım ve hâlâ her API'de uyguladığım ilkelerin özeti.

1. Sözleşme önce gelir

API'nin tüketicisi kod değil, insan ekiplerdir. Endpoint'in URL'i, HTTP metodu, durum kodları ve hata gövdesi bir sözleşmedir; bir kez yayımlandıktan sonra değiştirmek kırıcıdır. Bu yüzden:

  • Kaynak isimleri çoğul ve isim tamlaması: GET /orders/42/items
  • Fiiller HTTP metodlarından gelir; URL'de getOrder, createUser görüyorsanız bir şeyler ters gidiyordur.
  • Hata gövdesi makine tarafından işlenebilir olmalı: sabit bir code alanı + insan için message:
{
  "code": "ORDER_ALREADY_SHIPPED",
  "message": "Kargoya verilmiş sipariş iptal edilemez.",
  "requestId": "a1b2c3d4"
}
  • Kırıcı değişiklik kaçınılmazsa sürümleyin (/v2/orders); mevcut istemciyi sessizce bozmak yerine iki sürümü bir süre paralel yaşatın.

2. Doğrulamayı sınırda yap

Geçersiz veri sisteme ne kadar derin sızarsa, temizlemesi o kadar pahalı olur. Spring'de @Valid + Bean Validation anotasyonları (@NotBlank, @Size, @Email) ile doğrulamayı controller sınırında bitirin. İş kuralları (ör. "stok yetersizse sipariş açılamaz") servis katmanında; format kuralları sınırda.

Bu ayrım, durum kodlarının nerede doğduğunu da netleştirir:

İstek yaşam döngüsü: doğrulama sınırından servis katmanına akış ve her aşamada üretilen durum kodları

3. Durum kodları anlam taşır

  • 400 — istemci hatası: gövde bozuk, alan eksik
  • 401 / 403 — kimlik yok / yetki yok (ikisi farklı şeydir)
  • 404 — kaynak yok; ama yetkisiz erişimde bilgi sızdırmamak için de kullanılabilir
  • 409 — çakışma: aynı kaynağı iki kişi değiştiriyor
  • 500 — bizim hatamız; istemciye stack trace değil, korelasyon ID'si dönün

4. Idempotency'yi baştan düşün

Ağ güvenilmezdir; istemciler yeniden dener. GET, PUT, DELETE doğaları gereği idempotent olmalı. POST için kritik akışlarda (ödeme, sipariş) idempotency key deseni hayat kurtarır: aynı anahtarla gelen ikinci istek, işlemi tekrarlamak yerine ilk sonucu döner. Bunu telekom entegrasyonlarında yaşayarak öğrendim — mesaj bir kez daha gelir, her zaman.

5. Sayfalama ve filtreleme baştan tasarlanır

"Şimdilik hepsini dönelim" diye başlayan endpoint, prod'da 200 bin kayıtla çöker. Liste dönen her endpoint'e ilk günden page/size (veya cursor) ekleyin ve maksimum sayfa boyutunu sunucuda sınırlayın. Cursor tabanlı sayfalama, kayıt eklenip silinirken sayfa kaymalarını da önler — offset tabanlı sayfalamanın klasik tuzağı.

6. Gözlemlenebilirlik sonradan eklenmez

Her isteğe korelasyon ID'si verin, loglara ekleyin, yanıt başlığında dönün. Sorun bildirimi "dün öğleden sonra bir hata aldık" yerine "şu request-id başarısız" olarak geldiğinde, çözüm süresi saatlerden dakikalara iner. Spring tarafında bir HandlerInterceptor veya filter ile MDC'ye (Mapped Diagnostic Context) yazılan tek bir ID, tüm log satırlarını birbirine bağlar.

7. Zaman aşımı ve dayanıklılık

API'niz başka servisleri çağırıyorsa, her dış çağrının zaman aşımı olmalı — varsayılan "sonsuz bekle" üretimde kabul edilemez. Zaman aşımına yeniden deneme eklerken de dikkat: idempotent olmayan bir çağrıyı otomatik tekrarlamak, aynı siparişi iki kez açmak demektir. Retry + idempotency key her zaman birlikte düşünülmeli.


Bunların hiçbiri tek başına parlak bir fikir değil; işin sırrı hepsini, her seferinde, istisnasız uygulamakta. İyi API, sürprizi olmayan API'dir.