Facebook tarafından ücretsiz olarak sağlanan bu teknoloji, REST mimarilerinin (API’ler) istek oluşturmasına olanak tanır. Müşteri ile API arasında belirli sözleşmeler çerçevesinde veri iletimini sağlar. Ayrıca GraphQL REST API yazmak yerine kullanılabilecek bir sorgulama dilidir. GraphQL herhangi bir veritabanına bağlı değildir. Bu nedenle POSTGRESQL veya mongoDB ile kullanılabilir. Basit bir şekilde calışma mimarisi aşağıda belirttiğim görselde anlatıldığı gibi açıklanabilir.
Şimdi GraphQL iş ortamını oluşturalım ve bir kaç örnek ile GraphQL’in çalışma mantığını anlayalım. Bir RestFul yaklaşımı ile GraphQL yaklaşımı karşılaştırmak bu işlem için bence en açıklayıcı yöntem olabilir.
Bir RestFull Api oluşturduğumuzu düşünelim örnek olarak elimizde şu şekil bir yaklaşım olsun. RestFull Api kısmına çok girmeden basit bir şekilde açıklamak istesek:
kubilaybzk.com/books/:id
Bize döndüreceği bilgiler ise : title,genre,reviews,authold değerleri olsun.
kubilaybzk.com/authors/:id
Bize döndüreceği değerler ise : name, age, books,biography değerleri olsun
Rest API da istediğimiz verileri elde etmek için çok fazla sayıda end pointe istek atmak zorunda kalabiliyoruz. GraphQL ise sunucuda bulunan verilere erişebilmemiz için tek bir endpoint gerekli. RESTful API ında client üzerinden basitçe istediğiniz isteğe göre size bir obje döndürür. GraphQL ise istek atarken istenilen objeye ve o objenin hangi özelliğine erişmek istiyorsanız ona göre istek atabiliyorsunuz.
Yukarıda bahsetmiş olduğum Rest Api kısmında bir kitap ele aldığımızı düşünelim örnek olarak kitabın id propertysi 123 olsun . Bu kitabın yazarına ulaşıp tekrar bütün kitaplarını elde etmek için birden fazla sorgu atmamız gerekecektir fakat GraphQL ile tek sorguda istenen tüm bilgileri elde edebiliriz. Örnek olarak
Tek bir sorgu ile istediğimiz bütün kitapları yazdırabildik.
Başka bir örnek vermemiz gerekirse Bir Person Objemiz olduğunu düşünelim bu person objesi oluşturulurken name ve age olmak üzere 2 farklı değer almak zorunda olsun. Yani elimizde olan veri şu şekilde set edilmiş olsun.
type Person {
name: String!
age: Int!
}
Şimdi biz bu person datalarından sadece ama sadece isimleri yazdırmak istiyor olalım. Yapacağımız işlem ;
{
allPersons {
name
}
}
Şeklinde bir sorgu göndermek.Bu işlem sonrası GraphQL bize geri dönüş olarak ;
{
"allPersons": [
{ "name": "Kubilay" },
{ "name": "KubilayBzk" },
{ "name": "KubilayBozak" }
]
}
şeklinde 3 adet data göndermiş olacak bu dataları istediğimiz gibi kullanabiliriz. Özellikle bahsetmek istiyorum bildiğiniz gibi biz Person’u oluştururken isim ve yaş değerleri içerdiğini belirmiştik. Burada ise sadece isim değerlerini aldık.
Temel Kavramlar
Şema Tanımlama Dili (The Schema Definition Language (SDL))
GraphQL, bir API şemasını tanımlamak için kullanılan kendi tip sistemine sahiptir . Şema yazma söz dizimine Schema Definition Language (SDL) denir .
Basit bir örnek vermek gerekirse;
type Person {
name: String!
age: Int!
}
Örnekten bahsettiğimiz tip name ve age olmak üzere iki adet field içermektedir. Dikkat ederseniz String yazdıktan sonra ünlem işareti(!) belirtilmiş bu bu fields değerinin required olduğunu belirtir.
Birbiri ile ilişkili olan verilerde türler arasındaki ilişkiyi belli edebiliriz.Person
ile ilişkilendirilebilir Post değerlerinin ilişkili olduğunu düşünelim
type Post {
title: String!
author: Person!
}
Tersine ilişki mantığı ile ulaşmak istersek.
type Person {
name: String!
age: Int!
posts: [Post!]!
}
Belirtmeden geçemeyeceğim . Burada Person
ve Post
arasonda one-to-many relationship mantığı var yani person altında bir post arrayi mevcut.
Başka bir önemli avantajımız :
İç içe sorgu yapabiliyor olmamız örnek olarak medium’u ele alalım her bir kullanıcının postları olduğunu ve bu postların birer başlığa sahip olduğunu düşünelim. Sitede bulunan bütün postların başlıklarını yazdırmak istediğimiz zaman söyle bir bağlantı kurmamız gerekiyor . Kişiler → Kişilerin Postları → Postun Başlığı . Bunu GraphQL ile yazmak için :
{
allPersons {
name
age
posts {
title
}
}
}
olarak sorgulama yapabiliriz bu sayede istenen bütün datalara erişebiliriz.
Argümanlar ile Sorgulama .
GraphQL’de her eleman parametre alabilir . Bu tamamen sorgunun kodlama mantığına bağlı olarak değişkenlik gösterir. Örnek olarak yine person objesini ele alalım ve biz sadece son iki elemanı yazdıracak bir sorgumuz olduğunu ele alalım. Bizim amacımız sadece son iki elemanın adını yazdırmak olsun. Yapacağımız sorgulama söyle olmalı.
{
allPersons(last: 2) {
name
}
}
Aslında normal programlama gibi yani fonksiyon içinde parametre belirmek gibi düşünülebilir bu işlem .
Mutasyon kullanarak Data Yazma Update etme yada Silme
Dİğer bütün teknolojilerde olduğu gibi GraphQL kullanarak verileri silme update etme yada veri ekleme gibi işlemleri yapmak mümkün. Tek önemli nokta burada Mutations anahtar kelimesin kullanmamız gerektiği.
Bahsetmiş olduğum bu 3 işlem için sorgulama mutlaka ama mutlaka Mutations ile başlamak zorundadır ! Buna root field denir.
mutation {
createPerson(name: "Kubilay", age: 25) {
name
age
}
}
Yukarıda gördüğünüz gibi sorgumuz bir eleman ekleme işlemi yapıyor. Eklenecek kişinin bilgileri argüman olarak verilmiş ve fonksiyon mutasyon olarak oluşturulmuş.Yukarıdaki mutasyon için sunucu yanıtı aşağıdaki gibi görünecektir.
"createPerson": {
"name": "Kubilay",
"age": 25,
}
Ekleme yaparken id adında unique bir değişken olduğunu düşünelim . Yani properti değerlerimiz id , name ve age olarak düşünelim. Yani bizim sorgumuz şu şekilde olacak.
type Person {
id: ID!
name: String!
age: Int!
}
Data ekleme yaparken sorgulama’da yapabiliriz.
mutation {
createPerson(name: "Kubilay", age: 25) {
id
}
}
Bu şekilde eklenen elemanın id değerini sorgulayabiliriz.
Subscriptions ile gerçek zamanlı update
Günümüzde birçok uygulama için bir diğer önemli gereksinim, önemli olaylardan anında haberdar olabilmek için sunucuya gerçek zamanlı bir bağlantının olmasıdır. Bu kullanım durumu için GraphQL, subscribe olarak adlandırılan konseptini sunar .Bir istemci bir etkinliğe abone olduğunda, sunucuyla sabit bir bağlantı başlatacak ve sürdürecektir. Bu belirli olay gerçekte gerçekleştiğinde, sunucu ilgili verileri istemciye iletir. Tipik bir “ request-response-cycle” izleyen sorgular ve mutasyonların aksine , abonelikler müşteriye gönderilen bir veri akışını temsil eder .
Abonelikler, sorgular ve mutasyonlarla aynı sözdizimi kullanılarak yazılır Bir örnek vermemiz gerekirse
subscription {
newPerson {
name
age
}
}
Bir istemci bu aboneliği bir sunucuya gönderdikten sonra aralarında bir bağlantı açılır.Daha sonra mutasyon işlemi başladığı zaman yeni bir Person objesi oluşturur sunucu bu kişi hakkındaki bilgileri istemciye şu şekilde gönderir.
{
"newPerson": {
"name": "Kubilay",
"age": 25
}
}
Artık sorguların, mutasyonların ve aboneliklerin nasıl göründüğüne dair temel bir anlayışa sahip olduğunuza göre, hepsini bir araya getirelim ve şimdiye kadar gördüğünüz örnekleri uygulamanıza izin verecek bir şemayı nasıl yazabileceğinizi öğrenelim.
Şema bir GraphQL API ile çalışan en önemli kavramlardan biridir. API’nin yeteneklerini belirtir ve istemcilerin verileri nasıl talep edebileceğini tanımlar. Genellikle sunucu ve istemci arasında bir contract olarak görülür .
Genel olarak, bir şema basitçe GraphQL türlerinin bir derleme diyebiliriz. Ancak, bir API için şema yazarken bazı özel kök türleri vardır bunlar yukarıda bahsettiğim gibi Query , Mutation ve Subscription bunlar client tarafından gönderilen sorguların anahtar kelimesi diyebiliriz.
NASIL KULLANILIR ?
GraphQL’in ne olduğunu kendimce anlatmaya çalıştıktan sonra şimdi ise nasıl kullanılır kısmına geçiş yapalım . Kısaca olayı özetlemek gerekirse GraphQL, nesneler üzerinde belirli alanlar istemekle ilgilidir. Çok basit bir sorguya ve onu çalıştırdığımızda aldığımız sonuca bakarak başlayalım (örnek olarak verilecek bilgiler https://graphql.org/learn/queries/#fields web sitesinden alınmıştır. ) Burada Star Wars serisini içeren bir data kullanılmış. Konuyu kavramanın bu sayede daha kolay olacağını düşündüğüm için bu örnekleri kullanmayı seçtim.
hero {
name
}
}
Şeklinde bir sorgu çalıştırmak isteyelim. GraphQL bize
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
üst blok içinde gördüğünüz gibi bir sonuç döndürecektir. Dönen bu sonuç ile yapılan sorgunun hemen hemen aynı olduğunu fark etmişsinizdir.Bu GraphQL için önemlidir, çünkü her zaman istediğiniz sonucu geri alırsınız ve sunucu, istemcinin tam olarak hangi alanları istediğini bilir bizim verdiğimiz sorgulardan anlar .
Field isimimiz name:
Bize bir String döndürüyor.
Burada çekmek istediniz data isim ise isim şehir ise şehir yaş ise yaş yani kısaca ne isterseniz size onu döndürür.
Şimdi seride olan bütün karakterlerin arkadaşlarını listelemek isteyelim . Örnek olarak konunun uzamasını engellemek için tek bir karakter üzerinden konuyu dilim döndüğünce anlatmaya çalışayım . Mesela seriden bir karakterimiz olan “R2-D2" ‘nin arkadaşlarını listelemek isteyelim . Datalar içinde her karakterin düşman yada arkadaşlarını(dostlarını) içeren başka bir ilişkili database olduğunu ve buradan o verilere ulaşabildiğimizi ele alalım . Seride dost olarak gördüğü kişi sayısı birden fazla olduğu için mantıken bize birden fazla eleman döndürülmesi gerekir. Yani örnek olarak Luke Skywalker ve Han Solo bizim karakterimizin birer arkadaşıdır. Biz GraphQL’den bu bilgileri edinmek istediğimizde bize return değeri olarak [“Luke Skywalker”,”Han Solo”…] arrayini döndürmesi gerekir. Şimdi bunu GraphQL kullanarak test edelim.
{
hero {
name //Her bir karakterin isminin gelmesini istiyoruz.
friends {
name //Her bir karakterin arkadaşlarını bilmek istiyoruz.
}
}
}
Yukarıda göstermiş olduğum sorguyu çalıştırdığımız zaman GraphQL bize
{
"data": {
"hero": {
"name": "R2-D2", //Karakterimiz
"friends": [ //Arkadaşları
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
Şeklinde bir sonuç döndürecektir. (Konunun fazla uzamaması için tek bir karakter için dönen sonuçları yukarıda gösterilmiştir. )
GraphQL sadece istenen düz yani argüman içermeden sorgulama işlemi yapmaz bunun yanında argümana göre sorgulama yapabilir. Her karakterimizin bir “ID “ değeri olduğunu varsayalım ve bize GraphQL ile ID numarası 1000 olan bir karakterin boy bilgisini ve isimini öğrenmek için bir sorgulama yapalım.
Tahmin edeceğiniz gibi bir çok programlama dilinde olduğu gibi fonksiyonlarda argüman tanımlama işlemi oldukça benzer.
{
human(id: "1000") {
name
height
}
}
Yukarıda ID argümanı 1000 olan karakterin name ve height bilgilerine bir sorgu atıp erişmek istiyoruz. Daha Fazlası İçin Döküman
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
Şimdi elimde gerçek bir api olmadığı için başka bir Api kullanmaya karar verdim bu sayede olayı daha iyi anlatabileceğimi düşündüm yukarıda anlattıklarımı kullanarak sizde bu api üzerinden pratik yapabilirsiniz.
Weather API : https://github.com/konstantinmuenster/graphql-weather-api
Playground : https://graphql-weather-api.herokuapp.com/
Sol tarafta gördüğünüz gibi (name:”Istanbul” , country:”TR”) olmak koşulu ile id name coordination ve country sorgusunu yapmak istedik. Sağ tarafta ise sonucu elde edebildik.
Takma Adlar kullanarak Sorgulama (ALIASES)
Şimdi öncelikle normal bir sorgulama yapalım.
İstanbul dışında Ankara içinde bir sorgu yapmak isteyelim.
Sağ tarafta gördüğünüz gibi bir hata aldık kısaca hatada çakışma olduğunu bundan dolayı takma isim yani “ALIASES” kullanmamızı istiyor. Şimdi isimlendirme nasıl yapılır ondan bahsedelim. Return etmesini istediğimiz değerleri Fields değerlerinin isimlerini değiştirme şansı veriyor bize GraphQL örnek bunun için yapmamız gereken tek işlem
Yeni_İsim:Fields
Yeni_İsim: return_edecek değerin ismi
Böyle yazınca pek anlaşılır olmadığının farkındayım görseller ile açıklamaya çalışayım.
Öncelikle hatamızı gidermek için sorgularımıza isimler verelim ilk sorgumuza Marmara ikinci Sorgumuza ise Başkent diyelim. Gördüğünüz gibi sorgularımız çalıştı ve başarılı bir şekilde sonuç verdi.
Şimdi sorgularımızda istediğimiz değerler olan id,name,country ve coord datalarına takma isim ekleyelim. Marmara isimli sorgumuzda yeni isimlendirmelerimiz ile çıkan sonuçları Başkent isimli sorgumuzda ise default olarak ayarlanan değerleri görebilirsiniz.
Fragments
Türkçe karşılığı kırıntı bölme yada parçalama olarak çevrilebilir. Örneğimizi ele alalım gördüğünüz gibi Marmara ve Başkent olmak üzere iki adet takma isim eklediğimiz sorgumuz var ve ikisinde aynı bilgileri istedik. Sadece iki değil 3 tane belkide daha fazla sorgumuz olduğunu düşünelim ortaya çıkacak olan kod karmaşasını düşünebiliyor musunuz :)
Nispeten çok karmaşık bir sayfamız oldu. Şimdi bu karmaşıklığı engellememiz için GraphQL geliştiricileri çok güzel bir yol geliştirmiş.
fragment comparisonFields on ….{}
Dikkat ettiyseniz fragment comparisonFields on City yazdık fonksiyonumuz sonuna bizim kullandığımız weather-api için kullanılan objenin ismini verdik.
İşlem adı(Operation name)
Aslında oldukça basit biz şimdiye kadar, hem query
anahtar kelimeyi hem de query adını çıkardığımız bir söz dizimi kullanıyorduk , ancak üretim uygulamalarında, kodumuzu daha belirgin hale getirmek için operasyon isimleri kullanmak hem karmaşıklığı azaltır hemde kod okunurken kolaylık sağladığı için yararlıdır. Kendi uygulamamızda 3AdetSorgu adında bir isimlendime yapalım.
Gördüğünüz gibi play butonuna tıkladığımızda çıkan iki adet isim seçeneği var bu isimleri biz belirledik ve bu isimlendirmeler sayesinde hangi isimlendirme seçeneğinin çalışacağına karar verebiliyoruz. Örnek olarak paylaştığım yukarıda iki seçeneği seçtiğimiz zaman sağ tarafta buluna aaa bbb ccc sorgularının çalıştığını görebilirsiniz.
Variables kavramı ile dinamik sorgular
En çok işimize yarayacak ve biz front-end developerların mutlaka ama mutlaka kullanacağı en önemli kavramımızı anlatmaya başlayacağım.
Şimdiye kadar tüm argümanlarımızı sorgu dizesinin içine yazdık. Ancak çoğu uygulamada, alanlara yönelik argümanlar dinamik olacaktır: Örneğin bir login işlemi için kullanıcının form’dan yolladığı e-mail şifre ile bir token oluşturan bir api oluşturduğumuzu düşünelim. Her kullanıcı için bilgilerin tamamen farklı olacağı bundan dolayı oluşturacağımız sorgunun dinamik olması gerektiğini hemen fark etmişsinizdir. Öncelikle bilmemiz gereken bu sorgunun bir isminin olması gerektiğidir ve gönderilecek parametrelerin belirlenmesi ve Javascript formatında gönderilmesi gerektiğidir.
Bizim Api örneğimizin daha kavrayıcı olabilmesi için basit bir sorgu oluşturmaya çalıştım. Gördüğünüz gibi SeçilenŞehir adında bir sorgu oluşturduk. Daha sonra parantezler içinde gönderilecek olan parametreleri belirtiyoruz. Görselde gördüğünüz gibi dinamik olarak $cityname değişkenini oluşturduk. Daha sonra sorgu içine ne sorgulamak istiyorsak yazıyoruz bildiğiniz gibi biz id name country … gibi değişkenleri sorguluyoruz. Kullanmış olduğumuz playground’un özelliği olarak değişkenleri aşağıda belirttiğimiz gibi tanımlayabiliriz.
Değişken oluştururken tip belirtmek ve eğer o değişkenimiz required ise ünlem işareti (!) ile belirtmek zorundayız .
Mutasyonlar
Şimdiye kadar hep veri getirme ile ilgilendik peki veri düzenleme yada veri oluşturup database içine veri ekleme gibi işlemlerde ne yapacağız. Makalemin en başında bahsetmiştim fakat oldukça yüzeysel ve örnekler ile desteklenmemişti. Şimdi örnekler ile mutasyon kavramını açıklamaya çalışacağım .
Şimdi User eklememiz için geliştirilmiş bir Api geliştirildiğini düşünelim kullanıcı form içine username ve password bilgilerini girip submit ettikten hemen sonra bu apinın çalışacağını hayal edelim. Çalışacak olan Api sorgumuz şu şekil olmalıdır.
mutation AddNewUser ($username: String!, $password: String!) {
addUser(name: $name, password: $password) {
id
username
password
}
}
Sorgumuz return olarak nesnenin son halini döndürür. Bu, bir güncellemeden sonra bir nesnenin yeni durumunu almak için faydalı olabilir. Aşağıda ekleme bittikten sonra yapılan sorgumuzun return değerini görüyoruz.
{
"data": {
"addUser": {
"id": 1
"name": "KubilayBzk",
"password": "123456"
}
}
}
Şimdiye kadar elimden geldiğince açıklayıcı bir şekilde bilgilerimi aktarmaya çalıştım. Yazım hatam açıklarken oluşturduğum kafa karışıklıkları gibi durumlar oluştuysa kusuruma bakmayın sürç-i lisan ettiysek affola.