API Tasarımının Evrimi
Application Programming Interface (API), yazılım bileşenlerinin birbirleriyle iletişim kurmasını sağlayan arayüzlerdir. Web geliştirme tarihinde API tasarımı, SOAP'un karmaşık XML tabanlı yapısından REST'in basit ve kaynak odaklı yaklaşımına doğru büyük bir evrim geçirmiştir. Günümüzde ise GraphQL, REST'e güçlü bir alternatif olarak öne çıkmaktadır.
Her iki yaklaşımın da güçlü yönleri ve kullanım senaryoları vardır. Bu rehberde REST ve GraphQL'i derinlemesine inceleyecek, karşılaştıracak ve projenize en uygun yaklaşımı seçmenize yardımcı olacağız.
REST API Hatırlatması
REST (Representational State Transfer), Roy Fielding tarafından 2000 yılında doktora tezinde tanımlanan bir mimari stildir. HTTP protokolü üzerine inşa edilen REST, kaynakları (resources) URL'ler ile tanımlar ve bu kaynaklar üzerinde standart HTTP metodları (GET, POST, PUT, DELETE) ile işlemler gerçekleştirir.
REST'in Temel Prensipleri
- İstemci-Sunucu ayrımı: İstemci ve sunucu bağımsızdır, birbirlerinin iç yapısını bilmezler.
- Durumsuzluk (Stateless): Her istek, kendi başına yeterli bilgiyi taşır. Sunucu, istemci durumunu saklamaz.
- Önbelleklenebilirlik: Yanıtlar önbelleklenebilir olarak işaretlenebilir.
- Katmanlı sistem: İstemci, doğrudan sunucuya mı yoksa bir ara katmana mı bağlı olduğunu bilmek zorunda değildir.
- Tek tip arayüz: Kaynaklar URL'ler ile tanımlanır, temsiller üzerinden manipüle edilir.
Tipik REST API Örneği
GET /api/users → Tüm kullanıcıları listele
GET /api/users/1 → ID'si 1 olan kullanıcıyı getir
POST /api/users → Yeni kullanıcı oluştur
PUT /api/users/1 → ID'si 1 olan kullanıcıyı güncelle
DELETE /api/users/1 → ID'si 1 olan kullanıcıyı sil
GET /api/users/1/posts → Kullanıcının yazılarını getir
GraphQL Nedir?
GraphQL, Facebook tarafından 2012 yılında geliştirilmiş ve 2015 yılında açık kaynak olarak yayımlanmış bir sorgu dilidir. REST'ten farklı olarak, istemcinin tam olarak hangi verilere ihtiyaç duyduğunu belirlemesine olanak tanır. Tek bir endpoint üzerinden çalışır ve istemci, sorgusu ile yanıtın yapısını kendisi tanımlar.
GraphQL Şema Tanımı
GraphQL'de tüm veri yapısı bir şema (schema) ile tanımlanır. Şema, hangi tiplerin mevcut olduğunu, bu tiplerin hangi alanlara sahip olduğunu ve hangi sorguların yapılabileceğini belirler. Bu şema, istemci ve sunucu arasında bir sözleşme görevi görür.
# GraphQL Şema Tanımı
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
profile: Profile
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
createdAt: DateTime!
}
type Comment {
id: ID!
text: String!
author: User!
}
type Profile {
bio: String
avatar: String
website: String
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
posts(authorId: ID): [Post!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
createPost(input: CreatePostInput!): Post!
}
Sorgular (Queries)
GraphQL sorguları, istemcinin tam olarak hangi alanları istediğini belirtmesine olanak tanır. Aşağıdaki örnek, bir kullanıcının adını, e-postasını ve yazılarının başlıklarını tek bir sorguda getirir:
# GraphQL Sorgusu
query {
user(id: "1") {
name
email
posts {
title
createdAt
comments {
text
author {
name
}
}
}
}
}
REST ile aynı veriyi elde etmek için en az üç ayrı istek yapılması gerekirdi: kullanıcı bilgisi, yazıları ve her yazının yorumları. GraphQL bu problemi tek bir istekte çözer.
Mutasyonlar (Mutations)
Veri değiştirme işlemleri (oluşturma, güncelleme, silme) mutasyonlar ile gerçekleştirilir. Mutasyonlar, REST'teki POST, PUT ve DELETE metodlarının karşılığıdır.
# Mutasyon Örneği
mutation {
createPost(input: {
title: "GraphQL Rehberi"
content: "GraphQL hakkında kapsamlı bir rehber..."
authorId: "1"
}) {
id
title
createdAt
}
}
Abonelikler (Subscriptions)
GraphQL subscriptions, gerçek zamanlı veri güncellemeleri için kullanılır. WebSocket bağlantısı üzerinden çalışır ve sunucuda bir değişiklik olduğunda istemciye otomatik bildirim gönderir. Canlı sohbet, bildirim sistemleri ve gerçek zamanlı gösterge panelleri için idealdir.
# Abonelik Örneği
subscription {
newComment(postId: "1") {
text
author {
name
}
createdAt
}
}
Over-Fetching ve Under-Fetching Problemi
REST API'lerin en büyük sorunlarından biri, over-fetching (fazla veri çekme) ve under-fetching (eksik veri çekme) problemleridir. GraphQL, bu sorunları kökten çözmek için tasarlanmıştır.
Over-Fetching
REST'te bir endpoint, her zaman sabit bir veri yapısı döner. Örneğin, yalnızca kullanıcının adına ihtiyaç duysanız bile, kullanıcı endpoint'i tüm kullanıcı bilgilerini (ad, e-posta, adres, telefon, profil resmi vb.) döner. Bu durum, özellikle mobil uygulamalarda gereksiz bant genişliği tüketimi ve yavaş yükleme süreleri anlamına gelir.
GraphQL'de istemci yalnızca ihtiyaç duyduğu alanları belirtir. Sadece ad gerekiyorsa, yalnızca ad döner. Bu sayede ağ trafiği optimize edilir.
Under-Fetching
Tek bir sayfada birden fazla kaynaktan veri gerektiğinde, REST ile birden fazla istek yapılması gerekir. Örneğin bir kullanıcı profil sayfasında kullanıcı bilgileri, yazıları, takipçileri ve bildirimleri göstermek için dört ayrı API çağrısı yapılır. Bu durum, istemci tarafında karmaşık veri birleştirme mantığı ve çok sayıda HTTP isteği anlamına gelir.
GraphQL'de tüm bu veriler tek bir sorguda, iç içe ilişkiler belirtilerek çekilebilir.
N+1 Sorgu Problemi
GraphQL'in en bilinen performans sorunu N+1 sorgu problemidir. İlişkisel verilerde, bir liste sorgulanırken her eleman için ayrı bir veritabanı sorgusu tetiklenebilir. Örneğin 10 yazı ve her yazının yazarını getirirken, 1 (yazılar listesi) + 10 (her yazının yazarı) = 11 veritabanı sorgusu yapılır.
DataLoader ile Çözüm
Facebook'un geliştirdiği DataLoader kütüphanesi, bu problemi toplu yükleme (batching) ve önbellekleme ile çözer. Aynı request cycle'ında aynı tipteki istekleri toplar ve tek bir veritabanı sorgusunda çözer.
// DataLoader kullanımı
const userLoader = new DataLoader(async (userIds) => {
const users = await db.users.findByIds(userIds);
return userIds.map(id => users.find(user => user.id === id));
});
// Resolver'da kullanım
const resolvers = {
Post: {
author: (post) => userLoader.load(post.authorId)
}
};
Performans Karşılaştırması
| Kriter | REST | GraphQL |
|---|---|---|
| İstek sayısı | Çok (kaynak başına) | Az (tek endpoint) |
| Veri boyutu | Sabit (over-fetching riski) | Optimize (ihtiyaç kadar) |
| Önbellekleme | Kolay (HTTP cache) | Karmaşık (özel çözümler) |
| Dosya yükleme | Kolay (multipart) | Karmaşık (ek kütüphane) |
| Hata yönetimi | HTTP durum kodları | Her zaman 200, errors dizisi |
| Gerçek zamanlı | WebSocket/SSE ayrıca | Subscriptions dahili |
| Sürümleme | URL bazlı (v1, v2) | Gereksiz (şema evrimi) |
| Öğrenme eğrisi | Düşük | Orta-Yüksek |
Önbellekleme Stratejileri
REST'te Önbellekleme
REST API'lerde önbellekleme oldukça basittir. HTTP protokolünün yerleşik önbellekleme mekanizmaları (Cache-Control, ETag, Last-Modified) doğrudan kullanılabilir. Her kaynak benzersiz bir URL'ye sahip olduğu için, tarayıcı ve CDN önbelleklemesi otomatik olarak çalışır. Bu, REST'in en güçlü avantajlarından biridir.
GraphQL'de Önbellekleme
GraphQL'de tüm istekler genellikle tek bir endpoint'e POST metodu ile gönderildiğinden, HTTP düzeyinde önbellekleme doğrudan çalışmaz. Bunun yerine istemci tarafı önbellekleme çözümleri kullanılır.
- Apollo Client: Normalize edilmiş önbellek (normalized cache) kullanır. Her nesne, tipi ve ID'si ile önbellekte benzersiz olarak saklanır. Bir sorgu sonucu güncellendiğinde, aynı nesneyi kullanan diğer sorgular da otomatik güncellenir.
- Relay: Facebook'un geliştirdiği GraphQL istemcisi. Bileşen düzeyinde veri gereksinimleri tanımlanır ve otomatik olarak optimize edilmiş sorgulara dönüştürülür.
- Persisted Queries: Sorguların hash'leri sunucuya önceden kaydedilir. İstemci, tam sorgu metni yerine hash gönderir. Bu, ağ trafiğini azaltır ve CDN önbelleklemesini mümkün kılar.
GraphQL Araçları ve Ekosistemi
Apollo Platformu
Apollo, GraphQL ekosisteminin en kapsamlı platformudur. Apollo Server (sunucu tarafı), Apollo Client (istemci tarafı) ve Apollo Studio (izleme ve yönetim) bileşenlerinden oluşur.
// Apollo Server kurulumu
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = `#graphql
type Query {
users: [User!]!
user(id: ID!): User
}
type User {
id: ID!
name: String!
email: String!
}
`;
const resolvers = {
Query: {
users: () => db.users.findAll(),
user: (_, { id }) => db.users.findById(id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
console.log(`Sunucu hazır: ${url}`);
GraphQL Code Generator
GraphQL şemasından otomatik olarak TypeScript tipleri, React hooks'ları ve sunucu tarafı resolver tipleri oluşturan bir araçtır. Tip güvenliği sağlar ve geliştirici deneyimini önemli ölçüde artırır.
GraphQL Playground ve GraphiQL
API'yi interaktif olarak keşfetmek ve sorguları test etmek için kullanılan tarayıcı tabanlı IDE'lerdir. Şema inceleme, otomatik tamamlama, sorgu geçmişi ve dokümantasyon erişimi gibi özellikler sunar.
Ne Zaman REST, Ne Zaman GraphQL?
REST'i Tercih Edin Eğer:
- Basit CRUD işlemleri yapan bir API geliştiriyorsanız.
- HTTP önbelleklemesinden maksimum fayda sağlamak istiyorsanız.
- Dosya yükleme ve indirme işlemleri yoğunsa.
- Ekibiniz REST konusunda deneyimli ve GraphQL öğrenme süreci için zamanınız yoksa.
- Üçüncü taraf entegrasyonları için basit ve standart bir API sunmanız gerekiyorsa.
- Mikro hizmet mimarisinde servisler arası iletişim için basit bir çözüm arıyorsanız.
GraphQL'i Tercih Edin Eğer:
- Karmaşık ve ilişkisel veri yapılarınız varsa.
- Mobil uygulama gibi bant genişliğine duyarlı istemcileriniz varsa.
- Farklı istemcilerin (web, mobil, IoT) farklı veri ihtiyaçları varsa.
- Gerçek zamanlı veri güncellemeleri gerekiyorsa.
- Hızlı iterasyonlarla ön yüz geliştirmeniz gerekiyorsa (arka uç değişikliği olmadan).
- API sürümleme yönetiminden kaçınmak istiyorsanız.
Hibrit Yaklaşımlar
REST ve GraphQL'i birlikte kullanmak da mümkündür ve pek çok organizasyon bu yaklaşımı benimsemektedir.
GraphQL Gateway
Mevcut REST API'lerin önüne bir GraphQL katmanı eklenerek, istemcilere GraphQL arayüzü sunulurken arka planda REST servisleri kullanılabilir. Apollo Federation bu yaklaşım için güçlü bir çözüm sunar. Farklı ekiplerin bağımsız GraphQL şemaları, tek bir birleşik şema altında sunulabilir.
BFF (Backend for Frontend) Deseni
Her istemci tipi için ayrı bir backend katmanı oluşturulur. Web istemcisi için REST, mobil istemci için GraphQL kullanılabilir veya her ikisi de GraphQL kullanıp farklı şemalar sunabilir. Bu yaklaşım, her istemcinin veri ihtiyaçlarına özel optimize edilmiş API'ler sunmayı sağlar.
REST Endpoint'lerinde GraphQL Sorguları
Bazı REST endpoint'lerinde fields parametresi ile istemcinin istediği alanları belirtmesine izin verilebilir. Bu, GraphQL'in sağladığı esnekliğin basit bir versiyonudur ve mevcut REST API'lere kolayca eklenebilir.
Güvenlik Konuları
Her iki API yaklaşımının da kendine özgü güvenlik gereksinimleri vardır.
GraphQL Güvenlik Riskleri
- Sorgu derinliği saldırıları: İç içe sorgular ile sunucu kaynaklarını tüketme girişimleri. Sorgu derinliği sınırlaması ile önlenir.
- Sorgu karmaşıklığı: Çok sayıda alan ve ilişki içeren sorgular. Sorgu karmaşıklık analizi ve maliyet sınırlaması ile yönetilir.
- İntrospeksiyon: Üretim ortamında şema introspeksiyonunun devre dışı bırakılması önerilir.
- Oran sınırlama: REST'te URL bazlı kolay iken, GraphQL'de sorgu bazlı daha karmaşıktır.
Sonuç
REST ve GraphQL, birbirinin alternatifi olmaktan çok, farklı ihtiyaçlara cevap veren tamamlayıcı teknolojilerdir. REST, basitliği, geniş ekosistemi ve standart HTTP mekanizmalarını kullanması ile güçlü bir seçenektir. GraphQL ise esnekliği, tip güvenliği ve istemci odaklı tasarımı ile modern uygulama geliştirmede öne çıkmaktadır.
"En iyi API, kullanıcılarının ihtiyaçlarını en az sürtünme ile karşılayan API'dir. Teknoloji seçiminden önce, çözmek istediğiniz problemi ve hedef kitlenizi iyi anlayın."
Projeniz için doğru tercihi yaparken, ekibinizin deneyimini, uygulamanızın gereksinimlerini ve uzun vadeli hedeflerinizi dikkate alın. Hibrit yaklaşımları da göz ardı etmeyin; bazı senaryolarda her iki teknolojinin birlikte kullanılması en iyi çözüm olabilir.