GraphQL - REST'in Sınırlandırmalarından Kurtulun

Gbmdpof

Kıdemli Üye
23 Eyl 2016
2,001
11
GraphQL

Neden GraphQL?

Son yıllarda, bir web API tasarlarken REST, standart olarak kullanılmaya başlanılmıştı. Her ne kadar REST'in stateless sunucular (session bilgisi tutmayan sunucular) gibi güzel özellikleri olsa da REST, hiç esnek değil.

GraphQL'de burada araya girerek REST'in bu esnek olmama sorununu çözüyor ve bize çok daha esneklik ve efektiflik sunuyor. REST ile uğraşırken karşılaşılan birçok eksikliği çözerek bize çok daha güçlü bir API sağlıyor.

API'dan veri çekerken REST ve GraphQL'in en temel farklılıklarını göstermek için örnek bir senaryo düşünelim. Bir blog uygulaması, uygulama belirli bir kullanıcının verilerini göstermesi gerekiyor. Aynı ekran ayrıca bu kullanıcının son 3 takipçisini gösterecek. Bu soruna REST ve GraphQL ile nasıl çözüm buluruz?

REST API ile, bu işlem için büyük ihtimalle birden çok endpoint'e erişmemiz gerekecektir. Bu örnekle, bu endpoint'ler /users/<id>, /users/<id>/posts ve /users/<id>/followers olabilir.

y6NLRn.jpg

GraphQL'de ise bir istek ile birden fazla veri alabiliyoruz. Yani REST ile birden çok endpointe istek göndermemiz gerekirken GraphQL'de tek bir endpoint'e gönderdiğimiz isteği geliştirerek tüm verileri bir sorgu ile alabiliyoruz.

Emr56B.jpg

Bu örnekte de görebileceğiniz gibi tek bir istek ile kolayca birkaç veriye ulaşabiliyoruz.

Ayrıca sorguya ve dönen veriye dikkatli bakarsanız görebilirsiniz ki REST'teki gibi dönen sabit alanlar yok. Sadece istediğimiz alanlar dönüyor. Ne REST'teki gibi fazla veri alıyoruz ne de REST'teki gibi bir endpoint istediğimiz verileri göndermemesi sorunuyla karşılaşmıyoruz.

Nerelerde GraphQL uygun çözüm olmayabilir?

Genel olarak performans gerektiren yerlerle GraphQL biraz geride kalıyor. Öncelikle, REST API'larda sıkça gördüğümüz cache işlemi, GraphQL'de aynı sorguya rastlama ihtimalimizin oldukça az olduğundan GraphQL'de cache verimli olmuyor. Fakat yine de, Prisma ve Dataloader gibi kütüphaneler ile cache işlemi yapılabiliyor. Zira hala tamamen verimli olmuyor.

Bir diğer performans sıkıntısı ile kullanıcıya veri olarak ne alacağını seçme özgürlüğünü vermemizden kaynaklanıyor. Aşağıdaki örneğe bakarsanız ne diyeceğimi anlayacaksınız. Hem bir sanatçıyı, hem parçalarını, hem yorumlarını hem de yorum atan kullanıcı bilgilerini çekiyoruz. Bu da tabii sunucuyu zorluyor.

Kod:
artist(id: '1') {
  id
  name
  tracks {
    id
    title
    comments {
      text
      date
      user {
        id
        name
      }
    }
  }
}

Temel Konseptler

Schema Definition Language (SDL)

GraphQL, API şemalarını tanımlamak için kullanılan kendine özgü bir sisteme sahip. Şema yazmada kullanılan bu syntax, Schema Definition Language olarak adlandırılmaktadır.

Person adında SDL kullanılarak tanımlanan basit bir tür örneği:
Kod:
type Person {
  name: String!
  age: Int!
}

Bu tür, 2 alana sahip, ve bunlar name ve age olarak adlandırılmış. Birinin türü String ve diğeri de Int. Ayrıca ! işareti de bu alanın gerekli olduğunu belirtiyor.

Ayrıca burada oluşturduğumuz türleri başka yerlerde de kullanabiliriz. Örneğin Person türü, Post türünde kullanılabilir.

Kod:
type Post {
  title: String!
  author: Person!
}

Ayrıca bu ilişkinin diğer ucu da Person'da tanımlanmalı.

Kod:
 type Person {
  name: String!
  age: Int!
  posts: [Post!]!
}

(Buradaki köşeli parantez, posts alanının Post türünden oluşan bir liste olduğunu belirtmekte.)

Sorgular

Yukarıdaki örneklerde de gördüğümüz gibi GraphQL'in yaklaşımı genelde REST'e göre farklı. Birden çok endpoint yerine genelde tek bir endpoint üzerinden farklı sorgular ile farklı veriler çekiyor. Bu sorguları da özelleştirebildiğimiz için oldukça esneklik sunuyor bize.

O zaman GraphQL ile örnek bir sorguyu inceleyelim:

Kod:
{
  allPersons {
    name
  }
}

Bu sorgudaki allPersons alanı, sorgunun temel alanıdır. Bu temel alanın altına yazılan diğer her şey ise sorgunun payload'udur. Burada bu sorgunun payloadında belirlilen tek alan, name'dir.

Bu sorgu, veritabanındaki bütün kişileri döndürecektir. Örnek bir yanıta bakalım:

Kod:
{
  "allPersons": [
    { "name": "Johnny" },
    { "name": "Sarah" },
    { "name": "Alice" }
  ]
}

Fark edebilirsiniz ki yanıtta kişilerin sadece name verisi döndürülmüş. Bunun sebebi, sadece bu alanı sorgumuzda belirtmemizdir.

Fakat bu sorguyu birazcık geliştirip, kişilerin yaşlarını da almak istersek. Sorguya age alanını eklememiz yeterli olacaktır.

Kod:
{
  allPersons {
    name
    age
  }
}

GraphQL'in bir diğer büyük özelliklerinden biri de iç içe bilgi alımını desteklemesidir. Örneğin bir kişinin postlarını almak isterseniz aşağıdaki gibi iç içe (nested) bir sorgu yazarak hem kişi bilgisi alırken hem de post bilgisi alabilirsiniz.

Kod:
{
  allPersons {
    name
    age
    posts {
      title
    }
  }
}

Argümanlar

GraphQL'de bir alan, eğer şemada belirtildiyse argümanlara sahip olabilir. Örneğin allPersons alanı sadece belirli sayıda kişi döndürmesi için bir last parametresine sahip olabilir. Argümana sahip bir sorgu şu şekilde gözükecektir:

Kod:
{
  allPersons(last: 2) {
    name
  }
}

Mutasyonlar ile Veri Yazmak

Sunucudan veri okumak gibi bir çok uygulamada ayrıca sunucuda saklanan veride değişiklik yapmamızı da gerektiriyor. GraphQL ile bu değişiklikler, mutasyonlar ile yapılıyor.

Genel olarak üç tür mutasyon var:
  • yeni veri oluşturma
  • olan veriyi güncelleme
  • olan veriyi silme

Mutasyonlar da normal sorgular ile aynı yapıya sahipler, fakat bunlar her zaman mutation kelimesi ile başlamalı. Örneğin yeni bir kişi yaratabileceğimiz aşağıdaki örneği inceleyelim:

Kod:
mutation {
  createPerson(name: "Bob", age: 36) {
    name
    age
  }
}

Sorguya baktığınızda aslında bu sorgunun da önceki sorgularımızla çok benzer olduğunu göreceksiniz. Bunda da createPerson adında bir temel alan var ve daha önce öğrendiğimiz argümanlar da burada kullanılmış.

Ayrıca bir sorgu gibi yine bu mutasyonda da payload belirtebiliriz ve bu sayede yeni oluşan verinin propertylerine erişebiliriz. Bu örneğimizde bu istediğimiz veriler name ve age olduğu ve bunları zaten biz verdiğimiz için çok da işlevsel görünmeyebilir ama id gibi bizim bilmediğimiz verileri de alabilirdik.

Örnek bir yanıt ise bu şekilde:

Kod:
"createPerson": {
  "name": "Bob",
  "age": 36,
}

GraphQL'e özgü olarak karşılaşabileceğiniz bir özellik de GraphQL'in özel ID türlerinin olması. Bu türler sayesinde yeni bir nesne oluşturulduğunda, ID de otomatik olarak sunucu tarafından oluşturuluyor. Örneğin Person türümüzü şu şekilde geliştirebiliriz:

Kod:
type Person {
  id: ID!
  name: String!
  age: Int!
}

Artık yeni bir kişi oluşturulduğunda bu nesneye otomatik olarak bir ID de atanıyor ve sorgumuzda bu ID'yi isteyebiliyoruz:

Kod:
mutation {
  createPerson(name: "Alice", age: 36) {
    id
  }
}

Abonelik ile Anlık Güncellemeler

Günümüzde birçok uygulama, sunucuda bir değişiklik olduğu an hemen bilgi sahibi olma ihtiyacını duyabiliyor. GraphQL ise bu tür durumlar için abonelikler denen çok güzel bir konsept sunuyor.

Bir evente abone olunduğunda, sunucu sabit bir bağlantı oluşturacaktır. Bu event her meydana geldiğinde ise sunucu eşleşen veriyi gönderecektir. "istek-yanıt döngüsünü" takip eden sorgular ve mutasyonlardan farklı olarak abonelikler verilerin bir stream'i şeklinde gönderilmektedir.

Person türündeki eventlere abone olduğumuz bir örnek:

Kod:
subscription {
  newPerson {
    name
    age
  }
}

İstemci bu abonelik istediğini sunucuya gönderdikten sonra bir bağlantı sunucu ve istemci arasında açılacaktır. Bir yeni bir Person oluşturan mutasyon oluştuğunda ise sunucu yeni kişinin bilgisini gönderecektir:

Kod:
{
  "newPerson": {
    "name": "Jane",
    "age": 23
  }
}

Şema Oluşturmak

Artık sorgular, mutasyonlar ve abonelikler hakkında bilginiz olduğuna göre biraz da bu örnekleri çalıştırmamızı sağlayan şemaları nasıl yazabileceğimize bakalım. Şema, API'ın yapabileceklerini ve istemcinin veriyi nasıl isteyebileceğini açıkladığı için en önemli şeylerden biridir.

Genel olarak, bir şema GraphQL türlerinin bir koleksiyonudur. Fakat bir şema yazarken bazı özel temel türler de var.

Kod:
type Query { ... }
type Mutation { ... }
type Subscription { ... }

Bu Query, Mutation ve Subscription türleri, istemci tarafından gönderilen istekler için entry point'lerdir. Örneğin önceden gördüğümüz allPerson'a izin vermek için, Query türü şu şekilde yazılabilir:

Kod:
type Query {
  allPersons: [Person!]!
}

API'ın temel alanı, allPersons'tur. Daha önceden gördüğümüz last argümanını eklemek için ise şu şekilde değiştirebiliriz türü:

Kod:
type Query {
  allPersons(last: Int): [Person!]!
}

Mutasyonlar için de benzer şekilde Mutations türüne createPerson'u ekleyebiliriz:

Kod:
type Mutation {
  createPerson(name: String!, age: Int!): Person!
}

Fark edebilirsiniz ki temel alan, yeni kişi için gerekli olan name ve age argümanlarını alıyor.

Son olarak ise abonelikler için, newPerson temel alanını ekleyebiliriz:

Kod:
type Subscription {
  newPerson: Person!
}

Bunların hepsini bir araya getirirsek, bu konuda işlediğimiz tüm her şeyin tam bir şeması şu şekildedir:

Kod:
type Query {
  allPersons(last: Int): [Person!]!
}

type Mutation {
  createPerson(name: String!, age: Int!): Person!
}

type Subscription {
  newPerson: Person!
}

type Person {
  name: String!
  age: Int!
  posts: [Post!]!
}

type Post {
  title: String!
  author: Person!
}

 
Son düzenleme:

'Creative

Kıdemli Üye
16 Mar 2017
3,246
92
Elinize,emeğinize sağlık güzel konu olmuş neden ilgi görmemiş anlamadım.
 

Phoique 7

Katılımcı Üye
14 Mar 2017
505
1
Manisa
Elinize sağlık. Yakın zamanda kendisi araştırıp biraz bakmıştım. Güzel bir yapısı var.
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.