GraphQL, API geliştirme dünyasında son yılların en popüler teknolojilerinden biri haline geldi. REST mimarisinin getirdiği bazı kısıtlamaları aşmak ve istemcilerin ihtiyaç duyduğu veriyi daha esnek ve verimli bir şekilde almasını sağlamak amacıyla Facebook tarafından geliştirilen GraphQL, özellikle modern web ve mobil uygulamalarda veri alışverişini optimize etmek için güçlü bir alternatif sunar. Bu rehberde, Node.js ve Express kullanarak temel bir GraphQL API’sini nasıl oluşturacağınızı adım adım öğrenecek, pratik örneklerle konuyu pekiştireceksiniz. Amacımız, GraphQL’in temel prensiplerini anlamak ve kendi projelerinizde kullanmaya başlamak için sağlam bir temel oluşturmaktır.
GraphQL Nedir ve Neden Kullanmalıyız?
GraphQL, API’ler için bir sorgulama dili ve çalışma zamanıdır. Geleneksel REST API’lerinde genellikle birden fazla endpoint üzerinden farklı veri parçalarını almanız gerekirken, GraphQL’de istemci tam olarak ihtiyacı olan veriyi tek bir sorgu ile talep edebilir. Bu, “over-fetching” (gereğinden fazla veri çekme) ve “under-fetching” (gereğinden az veri çekme) gibi sorunları ortadan kaldırır ve ağ trafiğini azaltır.
GraphQL’in Avantajları:
- Veri Esnekliği: İstemciler tam olarak ihtiyaç duydukları veriyi sorgulayabilirler.
- Tek Endpoint: Tüm sorgular genellikle tek bir HTTP endpoint’ine (örneğin,
/graphql) yapılır. - Geliştirici Deneyimi: Güçlü tip sistemi sayesinde API’nin ne döndüreceği veya ne bekleyeceği net bir şekilde bellidir. Bu, hem backend hem de frontend geliştiricileri için daha iyi bir deneyim sunar.
- Daha Az Ağ İsteği: Karmaşık verileri tek bir istekte alarak performans artışı sağlar.
- Evrimsel API’ler: API’ye yeni alanlar eklemek veya mevcut alanları değiştirmek, istemcilerin mevcut sorgularını bozmadan yapılabilir.
Node.js ve Express Neden Tercih Edilmeli?
Node.js, JavaScript tabanlı, olay tabanlı ve asenkron yapısıyla hızlı ve ölçeklenebilir ağ uygulamaları geliştirmek için ideal bir çalışma zamanıdır. Express ise Node.js için minimalist ve esnek bir web uygulama çatısıdır. Bu ikili, GraphQL sunucusu kurmak için oldukça popüler ve güçlü bir kombinasyondur. Geniş bir paket ekosistemine sahip olmaları, geliştirme sürecini kolaylaştırır.
GraphQL Temelleri: Schema, Tipler ve Resolver’lar
Bir GraphQL API’si oluştururken anlamanız gereken en temel kavramlar Schema (Şema), Tipler (Types) ve Resolver’lardır.
Schema (Şema)
GraphQL şeması, API’nizin tüm yeteneklerini tanımlayan bir sözleşmedir. Hangi sorguların yapılabileceğini, hangi verilerin döndürüleceğini ve hangi veri tiplerini kullanacağınızı belirtir. Şema, GraphQL Schema Definition Language (SDL) kullanılarak yazılır. Her şema en az bir Query tipi içermelidir, bu tip istemcilerin API’den veri okumak için kullanabileceği tüm giriş noktalarını tanımlar. Veri değiştirmek için ise Mutation tipi kullanılır.
type Query {
hello: String
book(id: ID!): Book
books: [Book]
}
type Mutation {
addBook(title: String!, author: String!): Book
updateBookTitle(id: ID!, newTitle: String!): Book
}
type Book {
id: ID!
title: String!
author: String!
}
Yukarıdaki örnekte:
Querytipi,hello,bookvebooksadında üç sorgu tanımlar.Mutationtipi,addBookveupdateBookTitleadında iki işlem tanımlar.Booktipi, API’mizin döndüreceği kitap nesnesinin yapısını tanımlar.ID!,String!gibi ifadeler, alanın null olamayacağını (zorunlu olduğunu) belirtir.
Tipler (Types)
GraphQL, güçlü bir tip sistemine sahiptir. Bu, istemcilerin API’den hangi verileri bekleyebileceğini ve sunucunun hangi verileri işleyebileceğini net bir şekilde tanımlar. Temel (scalar) tipler şunlardır:
String: UTF-8 dizisi.Int: 32 bit işaretli tam sayı.Float: Çift hassasiyetli kayan nokta sayısı.Boolean:trueveyafalse.ID: Benzersiz bir tanımlayıcı (genellikle string olarak serileştirilir).
Kendi özel tiplerinizi de tanımlayabilirsiniz, yukarıdaki Book tipi gibi.
Resolver’lar (Resolvers)
Resolver’lar, şemadaki her bir alan için veri sağlayan fonksiyonlardır. Bir istemci bir sorgu yaptığında, GraphQL sunucusu bu sorguyu ayrıştırır ve ilgili resolver fonksiyonlarını çağırarak veriyi toplar. Örneğin, Query tipindeki books alanı için bir resolver, veritabanından tüm kitapları getirecek bir fonksiyon olabilir.
const books = [
{ id: '1', title: 'The Lord of the Rings', author: 'J.R.R. Tolkien' },
{ id: '2', title: 'The Hobbit', author: 'J.R.R. Tolkien' },
];
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find(book => book.id === args.id),
},
Mutation: {
addBook: (parent, args) => {
const newBook = { id: String(books.length + 1), title: args.title, author: args.author };
books.push(newBook);
return newBook;
},
updateBookTitle: (parent, args) => {
const book = books.find(book => book.id === args.id);
if (book) {
book.title = args.newTitle;
}
return book;
}
}
};
Bu resolver’lar, yukarıdaki şema ile uyumlu çalışır ve istemciden gelen sorgulara göre veriyi döndürür veya günceller.
Node.js ve Express ile GraphQL Ortamı Kurulumu
Şimdi pratik kısma geçelim ve basit bir GraphQL sunucusu oluşturalım.
Proje Oluşturma ve Bağımlılıkları Kurma
Öncelikle yeni bir Node.js projesi oluşturalım:
mkdir graphql-book-api
cd graphql-book-api
npm init -y
Ardından, gerekli bağımlılıkları yükleyelim:
npm install express graphql express-graphql
express: Web sunucumuzu oluşturmak için.graphql: GraphQL şemalarını ve resolver’larını tanımlamak için ana kütüphane.express-graphql: Express ile GraphQL’i kolayca entegre etmemizi sağlayan bir middleware.
Express Sunucusu ve GraphQL Middleware Entegrasyonu
Proje dizininde server.js adında bir dosya oluşturalım ve temel Express sunucumuzu kuralım:
// server.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const app = express();
// 1. GraphQL Şemasını Tanımlama (SDL kullanarak)
const schema = buildSchema(
type Query {
hello: String
}
);
// 2. Resolver'ları Tanımlama
const root = {
hello: () => 'Hello world!',
};
// 3. Express ile GraphQL Middleware'ı Kullanma
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true, // Geliştirme ortamında GraphQL Playground/GraphiQL arayüzünü açar
}));
const PORT = 4000;
app.listen(PORT, () => {
console.log(GraphQL API running at http://localhost:${PORT}/graphql);
console.log(Access GraphQL Playground at http://localhost:${PORT}/graphql);
});
Bu kodda:
expressveexpress-graphqlkütüphanelerini içe aktarıyoruz.buildSchemafonksiyonu ile GraphQL şemasını SDL string’inden oluşturuyoruz.rootnesnesi, şemadaki alanlar için resolver’ları içerir.app.use('/graphql', ...)satırı, Express sunucusuna/graphqlyoluna gelen istekleriexpress-graphqlmiddleware’ı ile işlemesini söyler.graphiql: trueayarı, tarayıcı üzerinden API’mizi test edebileceğimiz interaktif bir arayüz olan GraphQL Playground’u etkinleştirir.
Sunucuyu çalıştırmak için:
node server.js
Tarayıcınızda http://localhost:4000/graphql adresine giderek GraphQL Playground’u görebilirsiniz. Sağ taraftaki “Docs” bölümünden şemanızı inceleyebilir ve sol taraftaki sorgu alanına aşağıdaki sorguyu yazarak test edebilirsiniz:
query {
hello
}
Çıktı olarak {"data":{"hello":"Hello world!"}} almalısınız.
Pratik Uygulama: Kitap Yönetim API’si
Şimdi daha karmaşık bir örnek olan basit bir kitap yönetim API’si oluşturalım. Bu API, kitapları listeleme, tek bir kitabı alma ve yeni kitap ekleme yeteneklerine sahip olacak.
Veri Modeli
Basitlik adına, veritabanı yerine geçici olarak bellekte tutulan bir dizi kullanacağız:
// data.js
const books = [
{ id: '1', title: 'The Lord of the Rings', author: 'J.R.R. Tolkien' },
{ id: '2', title: 'The Hobbit', author: 'J.R.R. Tolkien' },
{ id: '3', title: 'The Alchemist', author: 'Paulo Coelho' },
];
module.exports = { books };
Şema ve Resolver’ların Güncellenmesi
server.js dosyamızı güncelleyelim:
// server.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const { books } = require('./data'); // data.js dosyamızı import ediyoruz
const app = express();
// GraphQL Şemasını Tanımlama
const schema = buildSchema(
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book]
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!): Book
updateBookTitle(id: ID!, newTitle: String!): Book
deleteBook(id: ID!): String
}
);
// Resolver'ları Tanımlama
const root = {
books: () => {
return books;
},
book: (args) => {
return books.find(book => book.id === args.id);
},
addBook: (args) => {
const newBook = {
id: String(books.length + 1), // Basit ID üretimi
title: args.title,
author: args.author,
};
books.push(newBook);
return newBook;
},
updateBookTitle: (args) => {
const bookIndex = books.findIndex(book => book.id === args.id);
if (bookIndex > -1) {
books[bookIndex].title = args.newTitle;
return books[bookIndex];
}
return null; // Kitap bulunamazsa null döndür
},
deleteBook: (args) => {
const initialLength = books.length;
const filteredBooks = books.filter(book => book.id !== args.id);
// Eğer kitap silindiyse, books dizisini güncelle
if (filteredBooks.length < initialLength) {
// books dizisini filtreledikten sonra güncelliyoruz
// Bu kısım gerçek bir uygulamada veritabanı işlemi olurdu.
// Array.prototype.splice kullanarak da yapılabilir.
// Basitlik adına yeni bir dizi oluşturmak yerine, var olanı değiştirelim:
books.splice(0, books.length, ...filteredBooks); // Eski diziyi yeni diziyle değiştir
return Book with ID ${args.id} deleted.;
}
return Book with ID ${args.id} not found.;
}
};
// Express ile GraphQL Middleware'ı Kullanma
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
const PORT = 4000;
app.listen(PORT, () => {
console.log(GraphQL API running at http://localhost:${PORT}/graphql);
console.log(Access GraphQL Playground at http://localhost:${PORT}/graphql);
});
API’yi Test Etme (GraphQL Playground ile)
Sunucuyu yeniden başlatın (node server.js) ve http://localhost:4000/graphql adresine gidin.
Tüm Kitapları Sorgulama (Query)
query {
books {
id
title
author
}
}
Belirli Bir Kitabı Sorgulama (Query)
query {
book(id: "1") {
title
author
}
}
Yeni Kitap Ekleme (Mutation)
mutation {
addBook(title: "Sapiens: A Brief History of Humankind", author: "Yuval Noah Harari") {
id
title
author
}
}
Bu mutasyonu çalıştırdıktan sonra tekrar books sorgusunu yaparak yeni eklenen kitabı görebilirsiniz.
Kitap Başlığını Güncelleme (Mutation)
mutation {
updateBookTitle(id: "1", newTitle: "The Lord of the Rings: Fellowship of the Ring") {
id
title
}
}
Kitap Silme (Mutation)
mutation {
deleteBook(id: "4")
}
Bu örneklerle, GraphQL’in temel sorgulama (Query) ve veri değiştirme (Mutation) yeteneklerini pratik olarak görmüş oldunuz.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Bu rehberde, Node.js ve Express kullanarak temel bir GraphQL API’sini nasıl oluşturacağınızı öğrendiniz. GraphQL’in temel kavramları olan Schema, Tipler ve Resolver’ları anladınız ve pratik bir kitap yönetim API’si örneği ile bunları uyguladınız. GraphQL’in sunduğu esneklik ve verimlilik, modern uygulama geliştirmede önemli avantajlar sağlar. Bu sadece başlangıç; GraphQL dünyası daha birçok gelişmiş konuyu (örneğin, abonelikler, kimlik doğrulama, veritabanı entegrasyonları, daha karmaşık tipler ve ilişkiler) barındırıyor.
Sıkça Sorulan Sorular (SSS)
S: GraphQL, REST’e göre neden daha iyi bir seçenek olabilir?
C: GraphQL, istemcinin tam olarak ihtiyacı olan veriyi tek bir istekte almasını sağlayarak “over-fetching” ve “under-fetching” sorunlarını ortadan kaldırır. Bu, daha az ağ trafiği ve daha hızlı yükleme süreleri anlamına gelir. Ayrıca, güçlü tip sistemi sayesinde API’nin dokümantasyonu otomatik olarak oluşturulur ve geliştirme deneyimi artar.
S: GraphQL’in dezavantajları nelerdir?
C: GraphQL’in öğrenme eğrisi REST’e göre biraz daha dik olabilir. Dosya yükleme gibi bazı işlemler REST’te daha basitken GraphQL’de ek çözümler gerektirebilir. Ayrıca, karmaşık sorguların performans yönetimi (N+1 problemi gibi) ve önbellekleme stratejileri REST’e göre farklı yaklaşımlar gerektirir.
S: GraphQL API’sinde kimlik doğrulama (authentication) ve yetkilendirme (authorization) nasıl yapılır?
C: GraphQL, kimlik doğrulama ve yetkilendirme için özel bir mekanizma sağlamaz. Bunlar genellikle Express’in kendi middleware’ları veya JWT (JSON Web Token) gibi endüstri standardı yöntemlerle yapılır. Örneğin, bir Express middleware’ı gelen isteğin başlığındaki token’ı doğrulayabilir ve kullanıcı bilgilerini GraphQL resolver’larına iletebilir.
app.use('/graphql', (req, res, next) => {
// Basit bir örnek: JWT token'ı kontrol et
const token = req.headers.authorization;
if (token && verifyToken(token)) { // verifyToken kendi fonksiyonunuz
req.user = getUserFromToken(token); // Kullanıcı bilgilerini ekle
}
next();
}, graphqlHTTP({
schema: schema,
rootValue: root,
context: ({ req }) => ({ user: req.user }), // Resolver'lara kullanıcıyı ilet
graphiql: true,
}));
Resolver içinde context objesi üzerinden kullanıcı bilgilerine erişerek yetkilendirme kontrolü yapabilirsiniz.
S: Veritabanı entegrasyonu nasıl yapılır?
C: Bu rehberde basitlik adına bellekteki bir dizi kullanıldı. Gerçek bir uygulamada, resolver’larınız veritabanı etkileşimlerini (örneğin, MongoDB için Mongoose, PostgreSQL için Sequelize gibi ORM/ODM kütüphaneleri kullanarak) gerçekleştiren asenkron fonksiyonlar olacaktır. Resolver’larınızda async/await kullanarak veritabanı işlemlerini kolayca yönetebilirsiniz.
// Örnek bir veritabanı entegrasyonu (pseudo-code)
const root = {
books: async () => {
return await BookModel.find(); // Mongoose ile tüm kitapları getir
},
addBook: async (args) => {
const newBook = new BookModel({ title: args.title, author: args.author });
await newBook.save();
return newBook;
},
};
Bu sayede GraphQL API’niz dinamik ve kalıcı verilere sahip olacaktır.
