GraphQL ve .NET

GraphQL API’ler için sorgulama desteği sunan bir query (sorgulama) dildir. API’ler için yani client-server iletişimini geleneksel yazı (text) ile değil obje yapısı ile sorgulama desteği sunar.

2012 yılında Facebook tarafından şirket içi proje olarak geliştirmeye başlandı ve 2015 yılında open source olması ile birlikte programlama dünyasına sert bir giriş yaptı. Böyle bir dil geliştirme sebepleri ise yine Facebook’un kullandığı native mobil uygulamalarda yaşanan veri transferi karmaşıklığını aza indirgemek.

GraphQL vs. REST

Bugün neredeyse projelerin çoğunu domine etmiş olan REST mimarisi ile GraphQL’in farklılıklarına bakalım.

HTTP mimarisinde bir endpoint (isteğin yapılacağı sunucuya ait servisin bağlantısı) vardır, bu endpoint’e yapılan isteğin sonucunda sunucudan bir cevap döner.

REST için; cevabın içerisinde sizin işinize yaramayacak değerler/alanlar da olabilir. Kısacası kocaman bir JSON yığını dönmesi mümkün. Ayrıca her bir isteğe yazılmış ayrı veya bir bilgi setini ekranda göstermek isterseniz birden fazla çağrı yapmız gerekir ve bir sürü endpoint’inizin de olması cabası. GraphQL’e karşı avantajlarından bir tanesi ise HTTP caching yapılabiliyor olması, GraphQL’de ise bunu aşmak gerekiyor.

GraphQL mimarisinde ise GraphQL sunucusuna istek sorgulanır ve sunucu sadece sorgulanan değerlerleri geri döndürür, koşul (argüman) belirtmenize de izin verir. REST’te ise parametre kullanmadıkça ya tümü dönecek ya da önceden tanımlı sayıda veya koşulda veri dönüşü olacak. Bir diğer farkı ise tek bir endpoint üzerinden tüm işlemlerinizi yapabiliyor olmanız. Elinizde bulunan veri çeşidi arttıkça endpoint’leriniz artmayacak, endpoint’lere ardı ardına istekte bulunulmayacak. Bir diğer avantajı ise “strongly-type” olması.

GraphQL uygulama seviyesinde sorgulama yapar veri tabanı seviyesinde değil. GraphQL “language-agnostic” olduğu için diğer programlama dillerinde GraphQL kullanan (client veya server) kütüphanelerin yazılması da GraphQL’i popüler kılan unsurlandan bir tanesi.

GraphQL kullananların yaptıkları yorumlar ise şu yönde: geliştirme süresi kısalıyor, veri azalıyor (boyut) ve performans artışı sağlanıyor. Bu yönleriyle REST’e karşı güçlü bir alternatif. Fakat REST’in bugün bir standart olduğunu da unutmayalım.

GraphQL kullanan firmalar ve bu firmaların hikayelerine göz atmak isterseniz buraya tıklayabilirsiniz.

GraphQL Şeması ve Tipler

GraphQL tüm backend uygulamalarda kullanılabilir olduğu için önceden tanımlanmış bazı değerler mevcut. Bunlardan bir tanesi tanımlı schema (şema) olması. Bu değerlerle GraphQL’i kullanacağız. Önce veri tiplerinden başlayalım.

GraphQL dokümantasyonunu incelemek isterseniz => https://graphql.org/learn/

Scalar Tipler

  • Int
  • Float
  • String
  • Boolean
  • ID (bir nesneyi elde etmek için veya cache amaçlı)

Örnek:

type Book {
   id: ID
   name: String! (non-nullable)
   price(unit: UnitPrice = TL): Float (argument)
   pages: Int
   available: Boolean
}

Enum Tipler

Enum tipler veri setlerinde belirli bir değerle sınırlıdır. Bu da bize kullanılan argümanın izin verilen değerler arasında olup olmadığını (validation) denetleme imkanı sunar.

Örnek:

enum booklanguage {
   TÜRKÇE
   KÜRTÇE
   JAPONCA
}

Query and Mutation Tipler

Bu tipler şema için giriş noktasıdır (entry point). Bunlardan iki tanesi: query ve mutation. Query sorgulama yapılacağı zaman (select), mutation ise veri üzerinde işlem yapılacağı zaman (ekleme/güncelleme/silme) kullanılıyor. Query ve mutation tipleri de diğer tipler gibi ifade edilir.

type Query {
   book_details: [Book]
}
type Mutation {
   addBook(name: String, price: Int): Book
}

GraphiQL

GraphQL’e hafiften giriş yaptık fakat ilerlemeden önce bir uygulamadan bahsetmek istiyorum. Yine GraphQL takımı tarafından geliştirilen, web tabanlı IDE GraphiQL (graphical interactive in-browser GraphQL IDE). Üzerinde sorgular yazıp çalıştırabildiğimiz bir web sayfası. GraphiQL open source ve incelemek isterseniz GitHub‘ta bulabilirsiniz.

GraphQL’e dair yazacağım tüm örnekleri GraphiQL üzerinde sorgulayacağız fakat bunun için GitHub GraphQL API kullanacağız. Özünde yine GraphiQL olan bu uygulama GitHub API ile entegre edilmiş ve uygulamayı kullanan herkesin gerçek ve canlı veriler üzerinde sorgular çalıştırmasına imkan veriyor. GitHub kullanıyor olmamızın sebebi ise hazır bir sürü API’nin olması ve hızlıca sorgularımızı çalıştırabileceğimiz bir uygulama olması.

Sayfaya girdiğinizde sizden “GraphQL API Explorer” uygulaması için izin vermeniz istenecek. Çünkü bu bir uygulama ve size ait verileriniz de sorgulanabilir (private repository vb.). Bu izni vermeniz gerekiyor yoksa sorgulama yapamazsınız.

Gerekli işlemleri yapıp sayfaya girdiğinizde aşağıdaki görselde bulunan görüntü ile karşılaşacaksınız.

Query

GitHub GraphQL API
GitHub GraphQL API

Kısaca ekranda neler olduğunu anlatayım:
Üst menüde üç adet buton bulunuyor bunlar: query sorgulama (execute), sorguyu minimal hale getirme (prettify) ve geçmiş (history).
Sol tarafta iki bölüm var: üstteki sorgularımızı yazacağımız alan, altındaki ise değişken tanımlama alanı (query variables).
Ortada, sorgu sonucunu gösteren bölüm var.
Sağda ise GitHub’ın API’larının neler olduğunu ve hangi değerleri istediği/döndürdüğünü gösteren bir dokümantasyon (documentation explorer) var.

Ekran görüntüsünde gördüğünüz veri canlı ve bana (sisteme giriş yaptığım hesap) ait. Sadece login bilgisini istediğim için tek bir değer döndürdü. Docs üzerinde viewer araması yaparsanız User sınıfı ile ilişkili olduğunu ve User altında diğer alanların da olduğunu görebilirsiniz.
Not
1. Herhangi bir tipe CTRL + MouseClick yaptığınızda Docs otomatik olarak o alanı önünüze getirecektir,
2. Eğer query tipini kullanır ve isim vermezseniz prettify düğmesine bastığınızda query ifadesinin silindiğini göreceksiniz.

Arguments

GraphQL’in avantajlarından bir tanesinin tek bir query ile istediğimiz veriye ulaşabiliyor olmamız. Bu sorgularda arguments yani parametre kullanmak mümkün. GitHub üzerinde beni takip eden kişilerden sadece iki kişinin, hesap oluşturulma tarihini gösteren bir sorgu hazırlayalım.

GraphQL Arguments
GraphQL Arguments

followers, beni takip eden kişilerin listesi. Yanında bulunan (first: 2) ise ilk iki hesabın sorgulanmak istediğini gösteriyor (argüman). followers‘a Docs üzerinden bakarsanız FollowerConnection tipinde nesne ile ilişkili. Onun altında ise nodes yani alt küme olduğunu göreceksiniz. O da User objesi ile ilişkili. viewer‘ı sorgularken kullandığımız obje ile aynı. Dolayısıyla yine aynı alanları sorgulayabiliriz. Örnekte görüldüğü gibi hesap oluşturulma tarihlerini sorguladım.

Aliases

Aynı alanı farklı argümanlarla sorguladığınızda conflict yani çakışma sorunu ile karşılaşacaksınız. Bunun önüne geçmek için alias yani takma ad/isimlendirme kullanmamız gerekiyor. Alias kullanımı [takmaAd]: [alan adı] şeklinde.
Aşağıdaki ekran görüntüsünde bir örneğini görebilirsiniz.

GraphQL Alias
GraphQL Alias

Fragments

GraphQL sorgunuzda aynı alanları birden fazla yerde kullanıyorsanız bunları tekrar yazmadan tek bir yerde toplayıp kod kalabalığından kurtulmak mümkün. Bu yöntemin adı fragment ve GraphQL dokümantasyonunda reusable units yani tekrar kullanılabilir birimler olarak açıklanıyor.

Aşağıdaki ekran görüntüsünde fragment yaratma ve kullanımı gösterilmektedir. Kullanımında fragment isminin önüne üç (3) nokta konuluyor.

GraphQL Fragment
GraphQL Fragment

Variables

Sorgularımızda değişken kullanmamız mümkün. Ekranda sol altta bulunan variable kısmı değişken tanımlamak ve tanımlanan değişkenleri sorguda kullanmak için var.

Bana ait olan repository’leri sorgulayacağım ve değişken olarak bu repository’nin public mi yoksa private mı olacağını belirteceğim. Değişkenimizi sorguda kullanırken başına dolar işareti ($) koymamız gerekiyor.

GitHub’da repository’lerinizi sorgularken RepositoryPrivacy tipinde değişken tanımlamız gerekiyor.

GraphQL Variables
GraphQL Variables

Not : Değişkenin sonunda bulunan ünlem işareti (!) bu değişkenin null olamayacağını işaret ediyor.

Mutation

Mutation’lardan girişte veri ekleme/silme veya güncelleme işleminde kullanıldığından bahsetmiştim.

GitHub Explorer üzerinde yapabileceğimiz en basit mutation örneğini göstereceğim. GitHub profilimin status (durum) alanını (profil sayfasında avatar’ın hemen altındaki kısım) güncelleyeceğim.

GraphQL Mutation
GraphQL Mutation

Bu işlemi gerçekleştirebilmek için dokümantasyona bakarsanız ChangeUserStatusInput kullanmamız gerekiyor. Bunun altında da clientMutationId denilen zorunlu alanı ve message yani asıl alanı kullanıyoruz.

Bu iki alanı Query Variables ekranında tanımladım. Sorgula düğmesine sırasıyla bastığımda (sorgu alanında birden fazla sorgu var ise size hangisini çalıştırmanız istediğini soracaktır) status alanının değiştiğini bunu da sonrasında çalışan getUserStatus sorgusuyla görebiliyoruz.

NOT : Yaklaşık 1-2 hafta önce GitHub Explorer üzerinde mutation işlemleri geçici süreliğine kapatılmıştı. Nedenini hatırlamıyorum fakat böyle bir döneme denk gelebilirsiniz, aklınızda olsun.
Ayrıca GitHub Explorer’ın da ara ara güncellendiğini hatırlatayım. Yeni tip veya mutation eklemeleri olabiliyor. İleride bu oyun sahası büyüyebilir veya değişiklik gösterebilir. Değişiklik listesi için => https://developer.github.com/v4/changelog/

.NET’e geçmeden önce son olarak; GraphQL topluluğu sayesinde sunulan video, kitap, blog veya slack gruplarına göz atmak isterseniz => https://graphql.org/community

GraphQL ve .NET

GraphQL’in “language-agnostic” olduğunu yani bir dile bağımlı olmadığını dolayısıyla her back-end uygulamada kullanılabilecek bir dil olduğunu belirtmiştik. Dil bağımsız olunca farklı programlama dilleriyle back-end uygulamalarda kullanılabilmesi için client ve server kütüphaneleri olması gerekiyor.

Facebook tarafından geliştirilen GraphQL kullanmak için hazırlanmış bir JS kütüphanesi mevcut: Relay fakat sadece React uygulamalarda çalışıyor.

GraphQL sayfasında diğer programlama dilleri için hazırlanmış kütüphanelerin bir listesi bulunuyor. Bu listede C#, Java, Go, Python vb. programlama dilleri mevcut. Listeye bakmak isterseniz => https://graphql.org/code/

Listede C#/.NET adı altında 3 adet kütüphane bulunuyor. Bunlar:
1. graphql-dotnet (GraphQL for .NET) (GitHub)
2. graphql-net (Convert GraphQL to IQueryable) (GitHub)
3. Hot Chocolate (GraphQL Server for .NET core and .NET classic) (GitHub)

GraphQL’i .NET ile beraber kullanırken listede ilk seçenek olan GraphQL.NET’i kullanacağım.
GraphQL.NET dokümanyasyonu => https://graphql-dotnet.github.io/docs/getting-started/introduction

Uygulama .NET Core Console uygulaması. Hem schema hem de query görevini aynı projede üstleneceğim (hem server hem client).

Uygulamaya geçmeden önce GraphQL’de tanımlı olan tiplerin GraphQL.NET’te .NET’te hangi tipe tekabül ettiğini görmek isterseniz => https://graphql-dotnet.github.io/docs/getting-started/schema-types

Bir kitap modelim olacak, bu modeli schema’da tanımlayacağım ve bu schema üzerinden sorgular çalıştıracağım.
GraphQL ve GraphQL.Types namespace’lerini kullanarak:

Yukarıdaki kod bloğunda sırasıyla
– bir kitap modeli yaratıyorum (ve enum)
– ardından bunu sorguda kullanabilmek için Graph tipinde bir kitap modeli yaratıp, generic tip olarak kitap modelimi veriyorum (aynısını enum için de yapıyorum)
– sorgumu (query) oluşturup tanımladığım Graph tipi objeleri kullanıyorum ve sorgulanması için örnek veri tanımlıyorum
– client (main metod içerisi) tanımlanan bu schema’ya (endpoint) kitabın alanlarını sorguluyor
ve sonuç (JSON)

{
  "data": {
    "book": {
      "id": 1,
      "name": "Fahrenheit 451",
      "category": "SciFi"
    }
  }
}

Az önceki örnekte bir kaç scalar tip (int, string, enum) kullandık. Şimdi hem complex tip hem de arguments kullanarak bir örnek yapalım. Yine kitap modeli üzerinden gideceğim fakat bu sefer kitap yorumları (BookReview) adlı modeli de yaratacağım.

Çıktı:

{
  "data": {
    "book": {
      "id": 1,
      "name": "Fahrenheit 451",
      "reviews": [
        {
          "id": 1,
          "review": "Muazzam."
        },
        {
          "id": 2,
          "review": "Korkutucu."
        }
      ]
    }
  }
}

Yukarıdaki kod bloğunda belirtilene göre eğer argüman tanımlamazsanız (query içerisinde parantez ile id verilen kısım) size veri dönüşü olmayacak ama hata almayacaksınız. Eğer argüman tanımlamadığında hata görmek isterseniz 83. satırda bulunan IdGraphType‘ı NonNullGraphType<IdGraphType> ile değiştirip deneyebilirsiniz.

Sırada variable (değişken) tanımlı sorgu kullanma var.

Çıktı:

{
  "data": {
    "book": {
      "id": "1",
      "name": "Fahrenheit 451"
    }
  }
}

Şimdi mutation yani veri manipülasyonu işlemi yapalım. Şu ana kadar örneklerimde kitaplar (books) listesinde id’si sırasıyla 1 ve 2 olmak üzere toplam 2 kitabım vardı. Şimdi id’si 3 olan bir kitap ekleyip bunu sorgulayacağım. Böylece veri ekleme işlemi yapmış olacağız. Bunun için InputObjectGraphType nesnesi oluşturmam lazım çünkü hatırlarsanız mutation için input’lar tanımlıyorduk.

Yukarıdaki mutation örneğinin çıktısı ise:

{
  "data": {
    "book": {
      "id": "3",
      "name": "Das Kapital"
    }
  }
}

Son olarak…

GraphQL’e başlamadan önce dokümantasyonu okumanızı öneririm çünkü basit bir dil ile her konu detaylıca işlenmiş. Burada işlemediğim bazı başlıklar (subscription, validation vb.) var evet, niyetim genel olarak ne olduğunu, olarak nasıl çalıştığını ve .NET dünyasında neye karşılık geldiğini göstermekti.

.NET uygulama hazırlıyor olmanız GraphQL.NET kullanımını zorunlu kılmıyor lakin şu an gördüğüm kadarıyla en başarılı kütüphanelerden bir tanesi. Kullanacaksanız, yine dokümantasyonunu okumanızı öneririm. Ayrıca dikkat ederseniz bir işi birden fazla yolla yapmanın mümkün olduğunu göreceksiniz.

Ve son olarak; yazdığınız tüm GraphQL sorgularını çalıştırmak için GitHub Explorer gibi bir oyun alanına ihtiyaç duyarsanız (ki bence mutlaka kullanın) GraphQL.Server.Ui.Playground NuGet paketini indirmenizi öneririm. Tanımladığınız tüm GraphQL objelerini burada kullanabilirsiniz. Tasarımı bile aynı olan playground’un görünümü aşağıdaki gibi.

GraphQL PlayGround
GraphQL Playground