REST mi GraphQL mi? İki API Tasarım Felsefesinin Gerçek Karşılaştırması
2015'te Facebook GraphQL'i açık kaynak yaptığında pek çok geliştirici "REST öldü" diye yazdı. 10 yıl sonra ikisi de hayatta, ikisi de yaygın kullanımda. Demek ki o kadar basit değil.
Her iki yaklaşım da API tasarımında geçerli. Ama farklı problemleri çözüyorlar. Birini diğerinin üstünde tutmak yerine, hangi durumda hangisinin mantıklı olduğunu anlamak daha değerli.
REST'in Problemi: Over-fetching ve Under-fetching
REST API tasarımının en yaygın şikayeti, verinin granülerliği üzerinde yetersiz kontroldür.
Over-fetching: İhtiyacınızdan fazla veri geliyor.
Bir kullanıcı listesi sayfası için sadece id, name, avatar alanlarına ihtiyacınız var. Ama GET /users endpoint'i email, phone, address, preferences, created_at, last_login, bio ve daha onlarca alanı da döndürüyor. Gereksiz veri ağ üzerinden taşındı, parse edildi, belleğe yüklendi.
Under-fetching: İhtiyacınız olan veri için birden fazla istek gerekiyor.
Bir kullanıcı profili sayfası oluşturuyorsunuz. Kullanıcı bilgisi için GET /users/123, gönderileri için GET /users/123/posts, takipçi sayısı için GET /users/123/followers — üç ayrı istek, üç ayrı round-trip.
// REST ile profil sayfası - 3 ayrı istek const [userData, postsData, followersData] = await Promise.all([ fetch("/api/users/123").then(r => r.json()), fetch("/api/users/123/posts").then(r => r.json()), fetch("/api/users/123/followers").then(r => r.json()) ]);
GraphQL: İstemci Veriye Şekil Veriyor
GraphQL'de istemci tam olarak hangi veriyi istediğini belirtir. Sunucu tam olarak o veriyi döndürür — ne fazlası ne eksiği.
# GraphQL ile aynı profil sayfası - tek istek query GetUserProfile($userId: ID!) { user(id: $userId) { id name avatar posts(limit: 5) { id title createdAt likesCount } followersCount } }
Tek HTTP isteği. Sadece istenen alanlar döner.
GraphQL'in Temel Kavramları
Schema: GraphQL API'sinin sözleşmesidir. Hangi veri tipleri var, hangi sorgular yapılabilir — hepsi schema'da tanımlı.
type User { id: ID! name: String! email: String! avatar: String posts: [Post!]! followersCount: Int! } type Post { id: ID! title: String! content: String! author: User! createdAt: String! likesCount: Int! } type Query { user(id: ID!): User users(limit: Int, offset: Int): [User!]! } type Mutation { createPost(title: String!, content: String!): Post! updateUser(id: ID!, name: String): User! deletePost(id: ID!): Boolean! }
Resolver: Her field'ı nasıl çözeceğini bilen fonksiyon.
const resolvers = { Query: { user: async (_, { id }, context) => { return context.db.users.findById(id); } }, User: { posts: async (user, { limit = 10 }, context) => { return context.db.posts.findByUserId(user.id, limit); }, followersCount: async (user, _, context) => { return context.db.follows.countByUserId(user.id); } }, Mutation: { createPost: async (_, { title, content }, context) => { if (!context.user) throw new Error("Unauthorized"); return context.db.posts.create({ title, content, authorId: context.user.id }); } } };
N+1 Problemi: GraphQL'in Aşil Topuğu
10 kullanıcı listesi dönerken her kullanıcının posts'u ayrı ayrı sorgulanırsa 1 + 10 = 11 veritabanı sorgusu çalışır.
// N+1 yaratan resolver User: { posts: async (user) => { return db.posts.findByUserId(user.id); // Her user icin ayri sorgu! } }
Çözüm: DataLoader — istekleri batch'ler ve tek sorguda getirir.
import DataLoader from "dataloader"; const postLoader = new DataLoader(async (userIds) => { const posts = await db.posts.findByUserIds(userIds); return userIds.map(id => posts.filter(p => p.authorId === id)); }); User: { posts: async (user) => { return postLoader.load(user.id); // Batchlendi, tek sorgu } }
REST mi GraphQL mi: Karar Rehberi
REST tercih edin:
Basit CRUD operasyonları için. Public API tasarlıyorsanız — REST daha geniş araç ekosistemi ve öğrenme eğrisi avantajı sunar. HTTP caching kritikse. Takım GraphQL deneyimine sahip değilse.
GraphQL tercih edin:
İstemci tarafında farklı veri ihtiyaçları varsa (mobil az veri, web daha fazla). Çok sayıda birbiriyle ilişkili varlık varsa. Hızlı geliştirme döngüsü önemliyse — frontend schema'ya bakarak backend bitmeden çalışabilir. Birden fazla istemci türü (web, mobil, TV) farklı veri ihtiyaçlarıyla aynı API'yi tüketiyorsa.
İkisi Birlikte: Hibrit Yaklaşım
REST ve GraphQL birbirini dışlamak zorunda değil. Pek çok büyük şirket ikisini birlikte kullanıyor: public API ve webhook'lar için REST, karmaşık iç sorgular için GraphQL.
GitHub bu yaklaşımın güzel örneği: hem REST API v3 hem GraphQL API v4 sunuyor. İkisi de aktif kullanımda.
API tasarımı araç seçiminden önce problem tanımıdır. "GraphQL mi REST mi?" sorusundan önce "istemcilerimin veri ihtiyaçları nasıl?" sorusunu cevaplayın.