Blog · 17 Jun 2024
REST vs GraphQL: Hangisini, Ne Zaman Seçmeli?
Bir API tasarlarken karşına çıkan ilk büyük kararlardan biri: REST mi, GraphQL mi? İkisi de "frontend ile backend nasıl konuşacak" sorusuna cevap veriyor ama yaklaşımları temelden farklı. Bu yazıda ikisini teorik tanımların ötesinde, gerçek örneklerle ve "ne zaman hangisi" sorusuna net cevaplarla karşılaştıracağım.
Hemen söyleyeyim: bu bir "X öldü, Y kazandı" yazısı değil. İkisinin de yeri var, ve doğru kararı projenin ihtiyacı belirliyor.
Önce kısa bir hatırlatma
REST, kaynakları (resource) URL'ler üzerinden temsil eden bir mimari yaklaşım. Her kaynağın kendi endpoint'i var ve HTTP metodlarıyla (GET, POST, PUT, DELETE) bu kaynaklar üzerinde işlem yapıyorsun.
restGET /users/42
GET /users/42/posts
POST /posts
DELETE /posts/17GraphQL, Facebook'un geliştirdiği bir sorgu dili. Tek bir endpoint'in var (/graphql) ve istemci tam olarak hangi veriyi istediğini sorgu içinde belirtiyor. Sunucu da sadece istenen veriyi döndürüyor.
graphqlquery {
user(id: 42) {
name
posts {
title
}
}
}Asıl fark: veriyi kim şekillendiriyor?
İki yaklaşım arasındaki en kritik fark bence bu. REST'te veriyi sunucu şekillendirir. Endpoint'i tasarlayan kişi, o endpoint'in ne döneceğine karar verir. GraphQL'de ise veriyi istemci şekillendirir. Frontend, ihtiyacı olanı tam olarak ister.
Bu fark, günlük geliştirmede üç somut soruna dokunuyor.
Over-fetching ve under-fetching
REST'te bir kullanıcının sadece adını isteyeceğin bir ekran için /users/42 çağırdığında, muhtemelen e-posta, kayıt tarihi, profil fotoğrafı, adres... hepsini birden alırsın. İhtiyacından fazlasını çekersin — buna over-fetching denir.
Tersi de var. Bir kullanıcıyı ve onun son 5 yazısını göstermek istiyorsun. REST'te genelde iki istek atarsın:
restGET /users/42
GET /users/42/posts?limit=5Tek istekte istediğin her şeyi alamazsın — buna under-fetching denir ve sonucu "N+1 istek" problemidir.
GraphQL bu iki sorunu da kökünden çözer. Tek sorguda, tam istediğin alanları, iç içe ilişkilerle birlikte alırsın:
graphqlquery {
user(id: 42) {
name
posts(last: 5) {
title
createdAt
}
}
}Tek istek, sıfır fazlalık. Özellikle mobil uygulamalarda, her byte ve her round-trip önemliyken bu büyük bir avantaj.
Versiyonlama
REST API'lerde alan ekleyip çıkardıkça versiyonlama derdi başlar: /v1/users, /v2/users... Eski istemcileri kırmamak için eski versiyonları yaşatmak gerekir.
GraphQL'de genelde versiyonlama yapmazsın. Yeni alan eklemek mevcut sorguları kırmaz; eski alanları da @deprecated ile işaretleyip zamanla emekliye ayırırsın. Schema sürekli evrilir ama tek kalır.
Tip güvenliği ve dokümantasyon
GraphQL'in en sevdiğim yanlarından biri: schema'nın kendisi bir sözleşme. Her alanın tipi belli, ve bu sayede otomatik dokümantasyon, otomatik tip üretimi (TypeScript ile harika çalışır) ve GraphiQL gibi interaktif keşif araçları "bedava" gelir. REST'te aynı seviyeye ulaşmak için OpenAPI/Swagger gibi ek katmanlar kurman gerekir.
Peki REST'in üstünlüğü nerede?
GraphQL'i bu kadar övdükten sonra dengeyi kuralım, çünkü REST hâlâ çoğu durumda doğru tercih.
Caching. Bu, REST'in en güçlü kozu. REST, HTTP'nin doğal caching mekanizmasıyla mükemmel uyumlu. GET /users/42 isteğini tarayıcı, CDN, ara katman... her yer cache'leyebilir. GraphQL'de her şey POST ile tek endpoint'e gittiği için bu otomatik caching çalışmaz; cache'i uygulama seviyesinde (Apollo Client gibi) kendin yönetmen gerekir.
Basitlik. REST'i herkes biliyor. Yeni bir geliştirici ekibe katıldığında GET /products ne yapar diye düşünmesi gerekmez. GraphQL'in öğrenme eğrisi daha dik: resolver'lar, schema tasarımı, N+1 problemine karşı DataLoader gibi konular...
Dosya yükleme, basit CRUD. İşin çoğu standart "oluştur-oku-güncelle-sil" ise GraphQL fazladan karmaşıklık getirir. REST burada daha doğrudan.
İzleme ve hız limiti (rate limiting). REST'te endpoint bazında metrik toplamak, log tutmak, rate limit koymak kolaydır. GraphQL'de her sorgu tek endpoint'e gittiği ve karmaşıklığı değişken olduğu için bunlar daha zahmetlidir — bir kullanıcı tek sorguda devasa, iç içe bir istek atabilir.
Aynı işi iki yaklaşımla yan yana
Diyelim bir blog ana sayfası yapıyorsun: yazar bilgisi + son 3 yazısı + her yazının yorum sayısı.
REST ile (genelde birden fazla istek):
javascriptconst user = await fetch('/api/users/42').then(r => r.json());
const posts = await fetch('/api/users/42/posts?limit=3').then(r => r.json());
const comments = await Promise.all(
posts.map(p => fetch(`/api/posts/${p.id}/comments/count`).then(r => r.json()))
);Üç (hatta daha fazla) round-trip, frontend'de birleştirme işi sana kalıyor.
GraphQL ile (tek istek):
javascriptconst { data } = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query {
user(id: 42) {
name
posts(last: 3) {
title
commentCount
}
}
}
`
})
}).then(r => r.json());Tek istek, veri zaten istediğin şekilde geliyor. Bu örnek GraphQL'in parladığı klasik senaryo: ilişkili, iç içe veriyi tek seferde toplama.
Karar tablosu
| Durum | Önerim |
|---|---|
| Basit CRUD, az ilişkili veri | REST |
| HTTP caching kritik | REST |
| Public API (üçüncü partiler kullanacak) | REST (daha tanıdık) |
| Karmaşık, iç içe, ilişkili veri | GraphQL |
| Çok farklı istemci (web + mobil + IoT) aynı API’yi kullanıyor | GraphQL |
| Frontend ekibi backend’e sürekli “şu alanı da ekler misin” diyorsa | GraphQL |
| Küçük ekip, hız öncelik, ekstra altyapı istemiyorsun | REST |
Sonuç
Benim pratik kuralım şu: şüphedeysen REST'le başla. Çoğu proje için yeterli, herkes biliyor, altyapısı hazır. GraphQL'e geçişi, gerçek bir acı hissettiğinde yap — frontend ekibi sürekli over-fetching'den şikayet ediyorsa, tek ekran için 5 ayrı istek atıyorsan, ya da çok çeşitli istemcilere çok çeşitli veri şekilleri sunman gerekiyorsa.
İkisi birbirinin düşmanı da değil. Birçok ekip ikisini birlikte kullanıyor: dış dünyaya açık, cache'lenebilir uçlar için REST; karmaşık iç ekranlar için GraphQL. Doğru cevap "hangisi daha havalı" değil, "bu projenin gerçek derdi ne" sorusunda.
Senin projende hangisini kullanıyorsun, neden? Yorumlarda merak ediyorum.