Nurettin Topal
5 min readNov 13, 2019

GraphQL 101: Yeni Başlayanlar İçin GraphQL

GraphQL, 2012 yılında Facebook tarafından geliştirilmiş ve 2015 yılında da açık kaynaklı hale getirilmiş API’ler için bir veri sorgulama ve manipülasyon dilidir.

REST API ya da Micro Service mimariler nedeniyle ortaya çıkan bazı sorunlara çözüm üretmek amacıyla kullanılmaktadır.

3 Temel Soruna Çözüm Üretir

  • İstemcinin(mobil, web, vb.) ihtiyacı olan veriyi ne eksik ne de fazla olacak şekilde, tam istediği kadar kısmını sunar.
  • Birden fazla kaynaktan veriye erişimi tek bir sorgulama ile sağlar.
  • Veri yapısını ve ilişkilerini tanımlayan bir yapı kullanır.

GraphQL temel olarak şu bileşenlerden oluşur: Schema, Type, Resolver.

Schema: GraphQL sizin veri yapınızı tanımlamak için bir şema yapısı kullanır. Buna sizin veri yapınızın modellenmesi de diyebilirsiniz. GraphQL, kendine özel bir Schema Definition Language(SDL) ile tanımlamaları kolay bir şekilde yapmanızı sağlar ve kolay okunabilir bir yapıda sunar. Şema yapısı sizin Input, Type, Query, Mutation, Subscription, vb. Type tanımlamalarınızın tamamını içerir.

Type:

1. Scalar Types: Çoğu programlama dilinden de bildiğiniz veri tipleridir. GraphQL’in varsayılanda desteklediği veri tipleri aşağıdaki şekildedir.

  • Int: 32 bit sayı değeri tutar.
id: Int
  • Float: Ondalıklı sayı değeri tutar.
amount: Float
  • String: UTF‐8 alfa numerik değeri tutar.
name: String
  • Boolean: true ya da false değerini tutar.
active: Boolean
  • ID: Alfa numerik benzersiz tanımlayıcı değerini tutmak için kullanılır. ID için Int olan 1 ile String olan 1 aynı anlama geldiği için genellikler UUID formatında değerleri tutmak için kullanılır.
uuid: ID
  • Input Type: Query, Mutation, Subscription’lara objeleri parametre olarak geçebilmek için kullanılan özel bir Object Type’lardır.
input UserCreateInput {
name: String
surname: String
}

2. Object Types: Scalar Type ya da başka bir Object Type tipinde alanlardan oluşur.

type User {
id: Int
name: String
surname: String
contact: Contact
}
type Contact {
phone: String
email: String
address: String
}

3. Query Type: GraphQL üzerinden ne tür sorgular(okuma) yapabileceğini tanımlamak için kullanılır.

type Query {
userSearch: [User]
}

4. Mutation Type: GraphQL üzerinden ne tür sorgular(yazma, güncelleme, silme) yapabileceğini tanımlamak için kullanılır.

type Mutation {
userCreate(input: UserCreateInput): User
}

5. Subscription Type: Gerçek zamanlı veri alış-veriş işlemleri için kullanılır.

type Subscription {
userCreated: User
}

Resolver: Schema’da tanımlı olan Query, Mutation ve Subscription’lar için(farklı resolver kullanım şekilleri de var fakat ileri aşama konuları olduğu için değinmiyorum) çalışacak fonksiyon tanımlamarını içerir diyebiliriz. Yani basit olarak her bir Query, Mutation ve Subscription için bir resolver’ımız bulunması gerekir ki, GraphQL’e gelen isteklerde verilerin hangi kaynaktan nasıl alınacağı vs. tanımlanabilsin.

#örnek javascript resolver koduQuery: {
userSearch() {
//DB'den ya da API'den veriler çekilir.
return [{ id: 1, name: "Hede", surname: "Hödö" }];
},
},
Mutation: {
userCreate() {
//DB'de ya da API'ye istek atılarak kayıt oluşturulur.
return { id: 1, name: "Hede", surname: "Hödö" };
},
},

İsimlendirme Standartları

GraphQL sunucu ya da istemci tarafında çeşitli diller kullanılmaktadır. JavaScript, JAVA, Kotlin, Swift, vb. Hepsinde de GraphQL için kabul edilmiş ortak standartlar aşağıdaki şekildedir.

  • Field isimleri camelCase olmalıdır.
  • Type isimleri PascalCase olmalıdır.
  • Enum isimleri PascalCase olmalıdır.
  • Enum değerleri ALL_CAPS olmalıdır.

Gerçek Problemler Üzerinden Çözüm Şekli

Kullanıcılar, kullanıcılara ait siparişler ve siparişlere ait ödemeleri içeren bi veri yapısı ve bunlar için yazılmış API’lerin olduğunu(ayrı microservice’ler ya da farklı endpoint’ler) düşünelim.

Sorun

İstemci bazı yerlede kullanıcı bilgilerini almak isterken(tamamını ya da bir kısmını), bazı yerlerde de kullanıcı bilgisi ile birlikte sipariş bilgilerini(tamamını ya da bir kısmını) de almak isteyebilir. Hatta bu siparişe ait ödeme bilgisini de almak istediği durumlar da olabilir.

Hangi durumlarda verinin ne kadarının dönüleceği ve bu verilerin ilişkili olduğu verilerin de dönülüp dönülmeyeceği, dönülecekse ne kadarı dönüleceği konusunu ana sorunumuz.

Bunu API tarafında çözmek istediğimiz zaman birbirine benzer bir çok API uç noktası açarak istemcinin ihtiyacına uygun olanı kullanmasını sağlayabiliriz. Alternatif olarak da bi uç noktası açarak hangi alanları ve ilişkili alanları istediğini de içerek bi istek göndererek almasını sağlayabiliriz. Sizin de tahmin edeceğiniz üzere hem istemci tarafında hem de API tarafında karmaşıklığa neden olacaktır.

Çözüm

Ne zaman hangi verileri ve bunlarla ilişkili verileri döneceğimizi kolay bir şekilde yönetmek için devreye GraphQL giriyor.

  1. 5 id’li kullanıcının adını, soyadını ve adresini istiyorum.
  2. 5 id’li kullanıcının adını, soyadını, email adresini, telefonunu, adresini ve geçen hafta yaptığı siparişleri, bu siparişlere ait ödeme bilgilerini.
  3. Geçen hafta gelen siparişlerin bilgilerini.
  4. Geçen hafta gelen siparişlerin bilgilerini ve bu siparişleri yapan kişilerin bilgilerini. ve siparişlere ait ödeme bilgilerini.
  5. Geçen hafta gelen siparişlerin bilgilerini ve bu siparişleri yapan kişilerin bilgilerini ile siparişlere ait ödeme bilgilerini.

Bu şekilde farklı ihtiyaçlar için istekleri çoğaltabiliriz. API ve istemci tarafında karmaşıklığa sebep olmadan, bunu kolay bir şekilde GraphQL aracılığı ile çözebiliyoruz. İstemci istek gönderirken hangi alanları istediği bilgisini de GraphQL’e bildirirse GraphQL de Schema’yı kullanarak gerekli olan verileri tek bir uç noktasından ya da birden fazla uç noktasından çekip istemciye vererek sorunu çözmüş oluyor.

{
//id'si verilen değere eşit olan kullanıcının bilgileri alınır
user(id: 5) {
id
name
surname
email
address
// bu kullanıcıya ait verilen tarih arasındaki siparişler alınır
orders(startDate: "2019-11-01", endDate: "2019-11-08") {
id
date
//orderlara ait payment bilgisi/bilgileri alınır.
payments {
id
bank
amount
date
}
}
}
}

İstemci tarafından gelen bu sorgu GrahpQL tarafında nasıl işlenir?

  1. API’ye yapılan bir istekle 5 id’li kullanıcının bilgileri alınır.
  2. API’ye yapılan bir istekle 5 id’li kullanıcının 2019–11–01 ve 2019–11–08 tarihleri arasındaki siparişleri alınır.
  3. Her bir siparişe ait ödeme bilgisi de sipariş sayısı kadar yapılan API isteği ile alınır.
  4. 1 ve 2 numaralı istekler aynı anda başlar, 3 numaları istek/istekler ise 2 numaralı isteğin cevabı geldikten sonra başlar. Bunu GraphQL yönetir.
  5. Gelen verilerden sadece sorguda belirtilen kısımlar istemciye dönülür.

İstemciye ulaşan veri ise şu şekilde gözükmektedir.

{
"id": 5,
"name": "Hede",
"surname": "Hödö",
"email": "hede@hodo.com",
"address": "istanbul",
"orders": [{
"id": 1,
"date": "2019-11-01",
"payments": [{
"id": 1,
"bank": "Garanti",
"amount": 10.50,
"date": "2019-11-01"
}]
}, {
"id": 2,
"date": "2019-11-02",
"payments": [{
"id": 1,
"bank": "Akbank",
"amount": 20.50,
"date": "2019-11-02"
}]
}]
}

Sonraki yazımızda örnek bir Nodejs GraphQL projesi oluşturmayı ve detaylı incelemesini ele alacağız. Ama dilerseniz örnek bir Nodejs GraphQL Server projesi için aşağıdaki GitHub reposunu inceleyebilirsiniz.

https://github.com/nurettintopal/nodejs-graphql-server

Umarım faydalı olmuştur…

GraphQL Server Serisi Yazıları

  1. GraphQL 101: Yeni Başlayanlar İçin GraphQL
  2. Node.js İle GraphQL Server Oluşturma
Nurettin Topal

Software Developer, Amateur Photographer, Traveller, Bookish, Amateur Sailor