API Nedir: REST mi GraphQL mi Tercih Etmelisiniz?
Bir sistem yöneticisi olarak yıllarca “bu bir backend meselesi, beni ilgilendirmez” diye düşündüm. Ta ki prod ortamında bir servis çöktüğünde API’nin nasıl çalıştığını bilmemenin ne kadar acı verici olduğunu yaşayana kadar. Monitoring kurarken, reverse proxy yazarken, load balancer ayarlarken API’nin anatomisini bilmek artık opsiyonel değil. Bu yazıda REST ve GraphQL arasındaki farkı, sysadmin gözüyle ele alacağız.
API Nedir, Gerçekten?
API, Application Programming Interface kelimelerinin kısaltması. Ama bu tanım hiçbir şey anlatmıyor. Daha somut konuşalım: iki sistemin birbiriyle nasıl konuşacağını tanımlayan sözleşme. Menüsü olan bir restoran gibi düşünün; mutfağa gidip doğrudan tencereye karışmıyorsunuz, garsondan sipariş veriyorsunuz. API de tam bu görevi yapıyor.
Sistem yöneticisi açısından bakıldığında API şu demek: bir endpoint var, bir istek geliyor, bir yanıt dönüyor. Bu isteğin ve yanıtın yapısını kim belirliyor? İşte burada REST ve GraphQL devreye giriyor.
REST: Eski Dostumuz
REST, Representational State Transfer’in kısaltması. 2000 yılında Roy Fielding’in doktora tezinde ortaya çıkmış. Bugün internet trafiğinin büyük çoğunluğu REST API’ler üzerinden akıyor.
REST’in temel fikri basit: her şey bir kaynak, her kaynağın bir URL’si var, HTTP metodları da bu kaynaklar üzerinde ne yapacağını söylüyor.
GET /users # tüm kullanıcıları listele
GET /users/42 # 42 ID'li kullanıcıyı getir
POST /users # yeni kullanıcı oluştur
PUT /users/42 # 42 ID'li kullanıcıyı güncelle
DELETE /users/42 # 42 ID'li kullanıcıyı sil
Bu yapı o kadar sezgisel ki öğrenmesi çok kolay. Bir URL’ye bakıp ne yaptığını anlamak için developer olmanıza gerek yok.
REST’in Avantajları
REST’in güçlü olduğu yerler var, bunları görmezden gelmemek lazım:
- HTTP’nin doğal yapısını kullanır: Caching, status code’lar, header’lar zaten HTTP’nin bir parçası
- Araç ekosistemi zengindir: curl, Postman, Insomnia gibi araçlarla direkt çalışırsınız
- Loglama kolaydır: Access log’lara bakıyorsunuz, hangi endpoint’e ne kadar istek gitmiş hemen görüyorsunuz
- Firewall kuralları yazmak basittir: Belirli endpoint’leri kısıtlamak veya izin vermek için PATH tabanlı kurallar yeterli
Bir REST API’ye curl ile istek atmak bu kadar basit:
# Basit bir GET isteği
curl -s https://api.example.com/users/42
-H "Authorization: Bearer TOKEN123"
-H "Accept: application/json" | jq .
# POST ile yeni kayıt oluşturma
curl -s -X POST https://api.example.com/users
-H "Content-Type: application/json"
-H "Authorization: Bearer TOKEN123"
-d '{"name": "Mehmet Yılmaz", "email": "[email protected]"}' | jq .
REST’in Gerçek Hayat Sorunları
Ama her şey güllük gülüstanlık değil. REST’in ciddi sorunları var ve bunlar özellikle büyük uygulamalarda baş gösteriyor.
Over-fetching problemi: Bir kullanıcının sadece adını ve e-posta adresini göstermek istiyorsunuz, ama /users/42 endpoint’i size 50 alan dönüyor. Gereksiz data aktarımı hem bant genişliği israfı hem de performans sorunu.
Under-fetching ve N+1 problemi: Bir kullanıcının siparişlerini göstermek için önce /users/42 çağırıyorsunuz, sonra /users/42/orders çağırıyorsunuz, her sipariş için /orders/{id}/items çağırıyorsunuz. 10 siparişi olan bir kullanıcı için onlarca HTTP isteği yapılıyor.
API versiyonlama kabusu: Bir field’ı değiştirmek istediğinizde /v1/users artık çalışıyor, /v2/users yeni yapıyı getiriyor. Zamanla /v1, /v2, /v3 yığını birikiyor, bunların hepsini maintain etmek zorunda kalıyorsunuz.
GraphQL: Farklı Bir Paradigma
GraphQL, Facebook (şimdiki adıyla Meta) tarafından 2012’de iç kullanım için geliştirildi, 2015’te open-source oldu. Facebook’un mobil uygulaması büyüdükçe REST’in yetersizlikleri ortaya çıkmış ve GraphQL bu soruna yanıt olarak doğmuş.
GraphQL’in temel felsefesi şu: istemci ne istediğini kendisi söylüyor. Tek bir endpoint var (/graphql genellikle), istemci bir sorgu gönderiyor, sadece istediği alanlar dönüyor.
Şöyle düşünün: REST’te garson size menüyü getiriyor ve siz “A seti” diyorsunuz, A setinin içinde ne varsa hepsi geliyor. GraphQL’de ise siz garsonla oturup “şundan istiyorum, bundan istemiyorum, bir de şunu ekle” diyebiliyorsunuz.
GraphQL Sorgu Anatomisi
# GraphQL sorgusunu curl ile göndermek
curl -s -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-H "Authorization: Bearer TOKEN123"
-d '{
"query": "{ user(id: 42) { name email orders { id total items { name price } } } }"
}' | jq .
Bu tek istek REST’te yapılacak birden fazla isteğin yerine geçiyor. Kullanıcı bilgisini, siparişlerini ve sipariş kalemlerini tek seferde çekiyorsunuz.
GraphQL’de üç temel operasyon tipi var:
- Query: Veri okuma işlemleri (REST’teki GET’e karşılık gelir)
- Mutation: Veri yazma, güncelleme, silme işlemleri (POST/PUT/DELETE’e karşılık gelir)
- Subscription: Gerçek zamanlı veri akışı (WebSocket benzeri)
# Mutation örneği - yeni kullanıcı oluşturma
curl -s -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-d '{
"query": "mutation { createUser(input: {name: "Ayşe Kaya", email: "[email protected]"}) { id name createdAt } }"
}' | jq .
GraphQL’in Sysadmin Gözüyle Avantajları
GraphQL’i özellikle microservice mimarilerinde çok kıymetli buluyorum. Şöyle bir senaryo düşünün: 5 ayrı backend servisiniz var ve bunları bir GraphQL gateway’in arkasına koydunuz. Frontend tek bir endpoint’e istek atıyor, gateway gerekli servisleri çağırıp sonuçları birleştiriyor. Bu mimarinin birkaç önemli avantajı var:
- Tek giriş noktası: Rate limiting, authentication, logging tek yerden yönetilir
- Tip güvenliği: GraphQL’in schema sistemi sayesinde hangi field’ın ne tipte olduğu net belli, bu monitoring açısından çok değerli
- Introspection: Schema’ya bakarak API’nin kendini nasıl belgelediğini görebilirsiniz
# GraphQL introspection sorgusu - API ne sunuyor öğrenmek için
curl -s -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-d '{
"query": "{ __schema { types { name kind description } } }"
}' | jq '.data.__schema.types[] | select(.name | startswith("__") | not)'
GraphQL’in Karanlık Yüzü
Ama burada dürüst olmak gerekiyor. GraphQL’in getirdiği bazı sorunlar var ve bunlar özellikle sistem yöneticilerini ilgilendiriyor.
Caching gerçek bir sorun: REST’te bir GET isteği doğası gereği cacheable. Nginx veya Varnish önünüze koyuyorsunuz, /users/42 için cache hit elde ediyorsunuz. GraphQL’de her şey POST isteği, aynı endpoint, farklı body. HTTP katmanında cache kurmak neredeyse imkansız, application seviyesinde çözmeniz gerekiyor.
Logging karmaşıklaşıyor: Access log’lara baktığınızda hep aynı şeyi görüyorsunuz: POST /graphql 200. Hangi sorgu ne kadar sürdü? Hangi operasyon kaç kez çağrıldı? Bunları öğrenmek için application seviyesinde ek logging şart.
N+1 problemi başka bir formda döner: İstemci derine giden sorgular yazabilir ve bunu kontrol etmezseniz database’i patlatırsınız. DataLoader pattern ve query depth limiting zorunlu hale geliyor.
# GraphQL query complexity ve depth kontrolü için örnek Nginx konfigürasyonu
# (application seviyesinde yapılması önerilir ama nginx'te body size limiti koyabilirsiniz)
cat /etc/nginx/conf.d/graphql.conf
server {
listen 443 ssl;
server_name api.example.com;
location /graphql {
# GraphQL için body size limiti - kötü niyetli büyük sorgulara karşı
client_max_body_size 1m;
# Rate limiting - aynı IP'den gelen istek sayısını sınırla
limit_req zone=graphql_limit burst=20 nodelay;
proxy_pass http://backend:4000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Gerçek Dünya: Hangi Durumda Ne Kullanmalı?
Teorik tartışmadan çıkıp pratik senaryolara bakalım.
REST tercih edin:
- Basit CRUD işlemleri yapan bir API kuruyorsanız
- Public API sunuyorsanız ve üçüncü parti geliştiricilerin kolayca kullanmasını istiyorsanız
- Dosya upload/download gibi binary veri transferi söz konusuysa
- Agresif HTTP caching’e ihtiyacınız varsa (CDN’den yararlanmak istiyorsanız)
- Ekibiniz REST’e hakim ve GraphQL öğrenme eğrisine vakit ayıramazsanız
GraphQL tercih edin:
- Birden fazla istemci tipi var (web, mobil, TV uygulaması) ve her biri farklı veri ihtiyacı duyuyorsa
- Microservice’leri bir gateway arkasında birleştiriyorsanız
- Frontend ekibi çok hızlı iterasyon yapıyorsa ve her seferinde backend’den yeni endpoint istiyorlarsa
- Bant genişliği kritikse (özellikle mobil uygulamalar için)
- Güçlü tip sistemi ve otomatik dokümantasyon istiyorsanız
Bir e-ticaret platformu için şöyle düşünebilirsiniz: ürün kataloğu, ödeme sistemi, kargo entegrasyonu gibi external partner entegrasyonları için REST kullanın. Mobil uygulama ve web uygulamasına veri sağlayan internal API için GraphQL tercih edin.
Monitoring ve Observability
İki API tipini monitor etmek farklı yaklaşımlar gerektiriyor.
REST için Prometheus metrics toplanması neredeyse standart hale geldi:
# REST API metriklerini Prometheus formatında çeken basit bir kontrol
curl -s http://api-server:9090/metrics | grep "http_request"
# API response time kontrolü
curl -o /dev/null -s -w "DNS: %{time_namelookup}snBağlantı: %{time_connect}snToplam: %{time_total}sn"
https://api.example.com/users/42
GraphQL için operasyon bazlı monitoring kurmanız gerekiyor. Apollo Studio veya benzeri araçlar kullanılıyorsa bu daha kolay, yoksa application’a özel middleware yazılması gerekiyor.
# GraphQL health check - genellikle özel bir endpoint açılır
curl -s -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-d '{"query": "{ __typename }"}' | jq .
# Beklenen yanıt: {"data": {"__typename": "Query"}}
Güvenlik Farklılıkları
REST’te güvenlik daha öngörülebilir. Belirli endpoint’lere belirli roller için erişim tanımlıyorsunuz, WAF kuralları yazıyorsunuz, rate limiting endpoint bazında uygulanıyor.
GraphQL’de durum daha karmaşık. Birkaç kritik nokta:
- Query depth limiting: Bir sorgunun ne kadar derine inebildiğini sınırlayın
- Query complexity limiting: Ağır sorguların sistemi yormamasını sağlayın
- Introspection’ı production’da kapatın: Schema’nızı düşmanlarınızla paylaşmayın
# GraphQL introspection'ın kapalı olup olmadığını test etme
curl -s -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-d '{"query": "{ __schema { types { name } } }"}' | jq .
# Eğer yanıt "Introspection not allowed" ise güvenlik doğru yapılandırılmış
# Eğer schema dönüyorsa production için kapatılması öneriliyor
REST’te SQL injection genellikle path parametrelerinde veya query string’de gelir ve WAF bunu yakalayabilir. GraphQL’de query body içinde geliyor, WAF bu detaya inmekte zorlanabilir. Application seviyesinde input validation şart.
Hibrit Yaklaşım: Neden Seçmek Zorunda Değilsiniz?
Büyük sistemlerde sıkça gördüğüm bir durum: tek bir cevap yok. Aynı sistemde hem REST hem GraphQL kullanılıyor. Bu mantıklı bir yaklaşım.
Örneğin webhook’lar için REST kullanırsınız (basit, anlaşılır, üçüncü parti uyumluluk), ana uygulama API’si için GraphQL kullanırsınız (esneklik, performans). İki dünyadan en iyisini almak mümkün.
Altyapı açısından bu iki API tipini aynı load balancer arkasında çalıştırmak sorun değil. PATH tabanlı yönlendirme yeterli:
# Nginx upstream konfigürasyonu - REST ve GraphQL ayrı backend'lere
upstream rest_backend {
server rest-service:3000;
keepalive 32;
}
upstream graphql_backend {
server graphql-service:4000;
keepalive 32;
}
# Nginx location bloklarıyla yönlendirme
location /api/v1/ {
proxy_pass http://rest_backend;
}
location /graphql {
proxy_pass http://graphql_backend;
}
Sonuç
Yıllarca “REST mi GraphQL mi?” sorusunun cevabı basitmiş gibi sunulduğunu gördüm. Değil. Her ikisinin de güçlü ve zayıf olduğu alanlar farklı.
Sistem yöneticisi olarak size düşen şunları bilmek:
- REST HTTP’nin doğal yapısıyla uyumlu, caching ve logging kolay, araç ekosistemi olgun
- GraphQL esneklik ve performans sunuyor, ama caching, logging ve güvenlik konfigürasyonu daha fazla dikkat istiyor
- Her ikisi de prod ortamında sağlıklı çalışabilir, altyapı konfigürasyonunuzu buna göre yapmanız gerekiyor
- Hibrit yaklaşım gerçek hayatta sık karşılaşılan ve makul bir tercih
Bir servis çöktüğünde hangi API tipini kullandığına bakmak zorunda kalırsınız. O an için hazırlıklı olmak, aralarındaki farkı bugünden anlamakla başlıyor.
