Takip et

GraphQL Nedir ve Neden Kullanmalıyız?

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ıtlamal

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:

  • Query tipi, hello, book ve books adında üç sorgu tanımlar.
  • Mutation tipi, addBook ve updateBookTitle adında iki işlem tanımlar.
  • Book tipi, 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: true veya false.
  • 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:

  • express ve express-graphql kütüphanelerini içe aktarıyoruz.
  • buildSchema fonksiyonu ile GraphQL şemasını SDL string’inden oluşturuyoruz.
  • root nesnesi, şemadaki alanlar için resolver’ları içerir.
  • app.use('/graphql', ...) satırı, Express sunucusuna /graphql yoluna gelen istekleri express-graphql middleware’ı ile işlemesini söyler.
  • graphiql: true ayarı, 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.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.