GraphQL API Nedir: REST ile Karşılaştırma
Modern web geliştirme dünyasında API tasarımı, uygulamaların omurgasını oluşturuyor. Yıllarca REST API’ler bu alanda tartışmasız hüküm sürdü. Ancak Facebook’un 2015 yılında GraphQL’i açık kaynak olarak yayınlamasıyla birlikte oyunun kuralları değişmeye başladı. Bugün birçok şirket GraphQL’e geçiyor, bazıları ise REST’te kalmayı tercih ediyor. Peki bu tercih nasıl yapılmalı? Bir sysadmin veya backend geliştirici olarak bu teknolojiyi gerçekten anlamak, hem doğru mimari kararlar vermeni sağlar hem de karşılaştığın sorunları daha hızlı çözmen için kritik öneme sahip.
REST API: Hâlâ Geçerli Olan Temel
REST (Representational State Transfer), HTTP protokolü üzerine inşa edilmiş bir mimari stildir. Kaynak odaklı yapısı, HTTP metodlarını (GET, POST, PUT, DELETE, PATCH) anlamlı şekilde kullanması ve durumsuzluk (statelessness) prensibi ile onlarca yıldır web servislerinin standardı oldu.
Tipik bir REST API şu şekilde çalışır:
# Kullanıcı bilgisi almak
GET /api/users/42
# Kullanıcının gönderileri
GET /api/users/42/posts
# Belirli bir gönderinin yorumları
GET /api/users/42/posts/7/comments
Bu yapı mantıklı ve anlaşılır görünüyor. Ancak gerçek dünya senaryolarında ciddi sorunlar baş gösteriyor.
REST’in Temel Sorunları
Over-fetching problemi: Bir kullanıcının yalnızca adını ve e-posta adresini göstermek istiyorsun. Ancak GET /api/users/42 endpoint’i sana 40 alanı olan bir JSON döndürüyor. Adres bilgileri, tercihler, istatistikler… Bunların hiçbirini kullanmayacaksın ama bant genişliğini tüketiyorlar, mobil istemcilerde pil ömrünü kısaltıyorlar.
Under-fetching problemi: Bir kullanıcının profilini, son 5 gönderisini ve takipçi sayısını tek ekranda göstermek istiyorsun. REST ile bunu yapmak için 3 ayrı endpoint’e istek atmak zorunda kalıyorsun:
# 3 ayrı HTTP isteği gerekiyor
GET /api/users/42
GET /api/users/42/posts?limit=5
GET /api/users/42/followers/count
Bu N+1 sorgusu sorununa benzer bir API versiyonudur ve özellikle mobil ağlarda gecikme problemi yaratır.
Versiyonlama karmaşası: REST API’lerde değişiklik yapmak genellikle yeni bir versiyon çıkarmayı gerektirir. /api/v1/, /api/v2/ şeklinde büyüyen endpoint listeleri, zamanla bakımı zorlaşan bir yapıya dönüşür.
GraphQL: Sorgu Dili Olarak API
GraphQL bir protokol değil, bir sorgu dilidir. İstemci, ihtiyaç duyduğu veriyi tam olarak belirtir; sunucu da yalnızca o veriyi döndürür. Tek bir endpoint üzerinden çalışır:
# GraphQL'de tek endpoint
POST /graphql
Yukarıdaki 3 ayrı REST isteği yerine GraphQL ile şunu yapabilirsin:
# GraphQL sorgusu - tek istekte her şey
curl -X POST https://api.example.com/graphql
-H "Content-Type: application/json"
-d '{
"query": "
query GetUserProfile {
user(id: 42) {
name
email
posts(limit: 5) {
title
createdAt
}
followersCount
}
}
"
}'
Tek HTTP isteği, tam istediğin veri. Ne fazlası ne eksiği.
GraphQL’in Temel Kavramları
GraphQL üç temel operasyon türüne sahiptir:
- Query: Veri okuma işlemleri (REST’teki GET)
- Mutation: Veri yazma, güncelleme ve silme işlemleri (REST’teki POST, PUT, DELETE)
- Subscription: Gerçek zamanlı veri akışı (WebSocket üzerinden)
Bir GraphQL şeması şu şekilde tanımlanır:
# Schema Definition Language (SDL) ile basit bir şema
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
followersCount: Int!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
createdAt: String!
comments: [Comment!]!
}
type Query {
user(id: ID!): User
users: [User!]!
post(id: ID!): Post
}
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String): User!
deleteUser(id: ID!): Boolean!
}
! işareti alanın null olamayacağını belirtir. Bu tür güvenliği, GraphQL’in en büyük avantajlarından biridir.
Gerçek Dünya Senaryosu: E-ticaret Uygulaması
Bir e-ticaret platformu düşün. Mobil uygulama, web sitesi ve üçüncü parti entegrasyonlar var. Her istemci farklı veri ihtiyaçlarına sahip.
REST yaklaşımında şunlar yaşanıyor:
Mobil uygulama ürün listesini görüntülemek için istek atıyor. Sunucu product_id, name, price, description, stock, category, images, reviews, related_products içeren devasa bir JSON döndürüyor. Mobil ekran yalnızca resim, isim ve fiyata ihtiyaç duyuyor. Diğer tüm veri boşa gidiyor.
GraphQL yaklaşımında ise mobil uygulama tam olarak neye ihtiyacı olduğunu soruyor:
# Mobil uygulama için optimize sorgu
query MobileProductList {
products(category: "electronics", limit: 20) {
id
name
price
thumbnail
}
}
# Web sitesi için daha detaylı sorgu
query WebProductDetail($id: ID!) {
product(id: $id) {
id
name
price
description
stock
images {
url
alt
}
reviews(limit: 5) {
rating
comment
author {
name
}
}
}
}
Aynı backend, farklı istemcilerin ihtiyaçlarını tek şema üzerinden karşılıyor. Ayrı mobil API, ayrı web API yazmak zorunda kalmıyorsun.
Node.js ile Basit GraphQL Sunucusu
Pratik bir örnek görelim. Express.js ve graphql-http kullanarak basit bir GraphQL sunucusu kuralım:
# Gerekli paketleri yükle
npm init -y
npm install express graphql graphql-http
npm install --save-dev nodemon
# Sunucu dosyasını oluştur
cat > server.js << 'EOF'
const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const { buildSchema } = require('graphql');
const schema = buildSchema(`
type Product {
id: ID!
name: String!
price: Float!
stock: Int!
}
type Query {
product(id: ID!): Product
products: [Product!]!
}
type Mutation {
createProduct(name: String!, price: Float!, stock: Int!): Product!
}
`);
// Örnek veri
const products = [
{ id: '1', name: 'Laptop', price: 25000.99, stock: 15 },
{ id: '2', name: 'Klavye', price: 850.00, stock: 50 },
];
const root = {
product: ({ id }) => products.find(p => p.id === id),
products: () => products,
createProduct: ({ name, price, stock }) => {
const product = { id: String(products.length + 1), name, price, stock };
products.push(product);
return product;
},
};
const app = express();
app.all('/graphql', createHandler({ schema, rootValue: root }));
app.listen(4000, () => {
console.log('GraphQL sunucu çalışıyor: http://localhost:4000/graphql');
});
EOF
node server.js
Sunucu çalışıyor. Şimdi sorgu atalım:
# Tüm ürünleri listele
curl -X POST http://localhost:4000/graphql
-H "Content-Type: application/json"
-d '{"query": "{ products { id name price } }"}'
# Yeni ürün oluştur
curl -X POST http://localhost:4000/graphql
-H "Content-Type: application/json"
-d '{
"query": "mutation { createProduct(name: "Mouse", price: 450.00, stock: 30) { id name price } }"
}'
# Beklenen çıktı:
# {"data":{"createProduct":{"id":"3","name":"Mouse","price":450,"stock":30}}}
Introspection: GraphQL’in Güçlü Silahı
REST API’lerde dokümantasyon için genellikle Swagger/OpenAPI kullanırsın ve bu dosyaların güncel kalması için ekstra efor gerekir. GraphQL’de ise şema kendi kendini belgeler. Introspection özelliği sayesinde istemciler şemayı sorgulayabilir:
# Şemadaki tüm tipleri sorgula
curl -X POST http://localhost:4000/graphql
-H "Content-Type: application/json"
-d '{
"query": "
{
__schema {
types {
name
kind
description
}
}
}
"
}'
# Belirli bir tipin alanlarını öğren
curl -X POST http://localhost:4000/graphql
-H "Content-Type: application/json"
-d '{
"query": "
{
__type(name: "Product") {
name
fields {
name
type {
name
kind
}
}
}
}
"
}'
Bu özellik, GraphiQL ve Apollo Studio gibi araçların interaktif dokümantasyon ve otomatik tamamlama sunmasını mümkün kılıyor. Yeni bir geliştirici sisteme katıldığında ayrıca dokümantasyon okumak zorunda kalmıyor; doğrudan şemayı keşfedebiliyor.
Performans: N+1 Sorunu ve DataLoader
GraphQL’in eleştirilen yönlerinden biri N+1 sorunu. Eğer dikkatli olmazsan her resolver bağımsız database sorgusu çalıştırır:
# Sorunlu senaryo: Her post için ayrı author sorgusu
# 100 post varsa 101 database sorgusu çalışır (1 post listesi + 100 author)
query {
posts {
title
author { # Her post için ayrı sorgu!
name
}
}
}
Bu sorunu çözmek için DataLoader kullanırsın:
# DataLoader kurulumu
npm install dataloader
# dataloader kullanım örneği
cat > loaders.js << 'EOF'
const DataLoader = require('dataloader');
// Kullanıcıları toplu olarak yükle
const userLoader = new DataLoader(async (userIds) => {
// Tek sorguda tüm kullanıcıları al
const users = await User.findAll({
where: { id: userIds }
});
// ID sırasına göre döndür (DataLoader için zorunlu)
return userIds.map(id => users.find(u => u.id === id));
});
module.exports = { userLoader };
EOF
# Resolver'da kullanım
# author: (post) => userLoader.load(post.authorId)
# Artık 100 post için tek bir SQL sorgusu çalışır!
echo "DataLoader sayesinde N+1 sorunu çözüldü"
DataLoader, aynı request içindeki benzer sorguları birleştirip tek database sorgusuna dönüştürür. Bu optimizasyon, GraphQL’i REST ile gerçekten rekabet edebilir kılan tekniklerden biridir.
Güvenlik Konuları
GraphQL’de güvenlik, REST’ten farklı bir boyut kazanıyor. İstemci istediği veriyi sorgulayabildiği için kötü niyetli sorgular ciddi sorunlar yaratabilir.
Derinlik sınırlaması: Sonsuz iç içe sorgu saldırısını önlemek için:
# Tehlikeli sorgu - sonsuz iç içe geçme
query MaliciousQuery {
user(id: 1) {
friends {
friends {
friends {
friends {
# Sonsuza kadar devam edebilir...
}
}
}
}
}
}
# graphql-depth-limit paketi ile koruma
npm install graphql-depth-limit
# server.js'e ekle
# const depthLimit = require('graphql-depth-limit');
# validationRules: [depthLimit(5)] // Maksimum 5 seviye derinlik
Sorgu karmaşıklığı sınırı:
# graphql-query-complexity paketi
npm install graphql-query-complexity
# Her alan için maliyet hesaplanır
# products(limit: 100) { reviews { comments { ... } } }
# gibi pahalı sorgular otomatik reddedilir
# Örnek maliyet konfigürasyonu:
# products: 10 maliyet puanı
# reviews: 5 maliyet puanı
# Toplam > 100 ise sorgu reddedilir
echo "Maksimum sorgu karmaşıklığı: 100 puan"
Rate limiting: REST’te endpoint bazında rate limiting uygularken GraphQL’de sorgu bazında uygulamalısın:
# nginx ile GraphQL endpoint'ine rate limiting
cat >> /etc/nginx/sites-available/api.example.com << 'EOF'
# GraphQL için özel rate limiting zone
limit_req_zone $binary_remote_addr zone=graphql:10m rate=30r/m;
location /graphql {
limit_req zone=graphql burst=10 nodelay;
proxy_pass http://localhost:4000;
proxy_set_header Content-Type application/json;
}
EOF
nginx -t && systemctl reload nginx
Caching: REST’e Göre Dezavantaj mı?
Bu noktada dürüst olmak gerekiyor. REST API’lerde HTTP caching son derece kolaydır:
# REST'te HTTP cache - basit ve etkili
# GET /api/products/1 isteği CDN tarafından cache'lenebilir
# Cache-Control, ETag, Last-Modified header'ları çalışır
# GraphQL'de tüm istekler POST ile gelir
# POST istekleri HTTP cache'leri tarafından cache'lenmez
# Apollo Client gibi araçlarla uygulama katmanında cache uygulanır
# Apollo Client cache konfigürasyonu (client-side)
cat > apollo-client.js << 'EOF'
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache({
typePolicies: {
Product: {
// ID bazında otomatik normalize edip cache'le
keyFields: ['id'],
},
},
}),
});
EOF
echo "Apollo Client uygulama seviyesinde akıllı cache yönetimi sağlar"
Persisted Queries yöntemi ile GraphQL sorgularını hash’e dönüştürüp GET isteği olarak gönderebilirsin. Bu sayede CDN caching mümkün hale gelir.
Ne Zaman GraphQL, Ne Zaman REST?
Her iki teknolojiyi de iyi anladıktan sonra, hangisini ne zaman kullanman gerektiğine dair net bir çerçeve oluşturalım.
GraphQL’i tercih et:
- Birden fazla istemci türü (mobil, web, TV, IoT) aynı backend’i kullanıyorsa
- Frontend ekibi backend’den bağımsız hareket etmek istiyorsa
- Veri gereksinimleri sık değişen, hızlı iterasyon yapılan projelerde
- Karmaşık, birbiriyle ilişkili veri yapıları söz konusuysa
- Gerçek zamanlı özellikler (chat, bildirimler, canlı güncellemeler) gerekiyorsa
REST’i tercih et:
- Basit CRUD işlemleri yeterli olan projeler için
- Güçlü HTTP caching ihtiyacı varsa (CDN optimizasyonu kritikse)
- Ekip GraphQL konusunda deneyimsizse ve öğrenme eğrisi kabul edilemiyorsa
- Dosya yükleme gibi işlemler temel işlevse
- Üçüncü parti entegrasyonlar REST API bekliyorsa
İkisini birlikte kullan:
Büyük şirketlerin çoğu hibrit yaklaşım benimsiyor. Yeni özellikler GraphQL ile, mevcut entegrasyonlar REST ile devam ediyor. GraphQL gateway (Apollo Federation, Hasura) arka planda REST API’leri çağırırken istemciye tek bir GraphQL arayüzü sunabiliyor.
# Apollo Federation ile mikroservis GraphQL mimarisi
# Her servis kendi şemasını yönetir
# Gateway bunları birleştirir
# users-service/schema.graphql
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
# orders-service/schema.graphql
type Order {
id: ID!
user: User! # users-service'den gelir
total: Float!
items: [OrderItem!]!
}
Monitoring ve Debugging
GraphQL’de hata ayıklama ve izleme, REST’ten biraz farklı çalışır.
# GraphQL'de her sorgu HTTP 200 döner, hata body'de gelir
# Bu monitoring araçlarını kandırabilir!
# Başarılı response:
# {"data": {"user": {"name": "Ali"}}}
# Hatalı response (hâlâ HTTP 200!):
# {"errors": [{"message": "User not found", "path": ["user"]}]}
# Nginx log'larını analiz etmek için özel format
cat >> /etc/nginx/nginx.conf << 'EOF'
log_format graphql_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time';
EOF
# GraphQL operasyon adını header olarak gönder ve log'a ekle
# Apollo Client otomatik olarak Apollo-Operation-Name header'ı ekler
Prometheus ve Grafana ile GraphQL metriklerini izlemek için graphql-request-duration-seconds gibi özel metrikler tanımlamalısın. Operasyon adına göre yavaş sorguları tespit etmek, REST’te endpoint bazında yapılan analizin GraphQL karşılığıdır.
Sonuç
GraphQL ve REST birbirinin rakibi değil, farklı ihtiyaçlara hitap eden araçlar. REST, 20 yıllık savaştan çıkmış olgun, iyi anlaşılmış ve mükemmel araç desteğine sahip bir mimari. GraphQL ise özellikle karmaşık veri ihtiyaçlarını, çoklu istemci senaryolarını ve hızlı iterasyon gerektiren projeleri yönetmekte üstün.
Sysadmin perspektifinden bakıldığında GraphQL’in getirdiği operasyonel değişiklikler önemli. HTTP caching stratejilerini yeniden düşünmek, N+1 sorgularını izlemek, sorgu karmaşıklığını sınırlamak ve introspection endpoint’ini production’da güvenli tutmak gibi yeni sorumluluklar geliyor.
Ancak getirdiği faydalar da göz ardı edilemez. Tek endpoint yönetimi, otomatik şema dokümantasyonu, tip güvenliği ve frontend ekibine tanınan özerklik, uzun vadede geliştirme hızını ciddi ölçüde artırıyor.
Teknoloji seçiminde her zaman “bu bizim sorunumuzu çözüyor mu?” sorusunu sor. Mevcut REST API’lerin over-fetching, under-fetching veya versiyonlama sorunu yaşıyorsa GraphQL ciddi bir alternatif. Yoksa “herkes kullanıyor” diye geçiş yapmak, gereksiz karmaşıklık ve ekip için öğrenme yükü anlamına gelir.
Her iki teknolojiyi de denemek için en iyi yol, küçük bir proje veya yeni bir microservice üzerinde her ikisini de uygulamak. Teoride her şey güzel görünür; gerçek deneyim farklı bir şey öğretir.
