İçeriğe geç →

GraphQL’e Giriş ve ASP.NET Core 2.0 ile Basit Bir Query API Tasarlamak

Merhaba arkadaşlar.

Bir süredir yeni bir makale yazmaya fırsat bulamadım. Hatta bu makalenin bir kısmını ise Ağustos ayında yazmıştım, fakat bir türlü tamamlayamamıştım. 🙂 Bulduğum ilk fırsatta ise tamamlamayı başardım.

Herneyse, sanırım GraphQL (ayrıca Asp.NET Core 2.0), veri erişimi ve sorgulama üzerine son dönemlerin en popüler konuları arasındadır. GraphQL özellikle günümüz çağında hızla gelişen bu business ihtiyaçlarına karşı, veri erişimini ve gelişmiş sorgulamasını client tarafına bırakarak, hızlı ve efficient bir şekilde development yapabilme imkanını biz developer’lara sunmaktadır.

Öncelikle GraphQL konusu, benim içinde yeni sayılabilecek bir konu. GraphQL ile henüz production üzerinde bir tecrübe edinemedim. Fakat yakın bir dönem içerisinde özellikle mobile API’larımız için bir gateway gibi GraphQL ile veri erişimi ve sorgulama işlemlerini gerçekleştirebilmeyi planlıyoruz. (Şuan üzerinde çalışıyoruz)

Bu makale içerisinde ise ASP.NET Core 2.0 ile GraphQL‘i kullanarak, sorgulama işlemlerini efficient bir şekilde nasıl gerçekleştirebiliriz konusuna, araştırmalarım sırasında edinebildiğim bilgiler doğrultusunda değinmeye çalışacağım.

Peki, Neden GraphQL?

Neden GraphQL sorusuna geçmeden önce, bir konuya değinmek istiyorum. Bir çok kişi REST‘in öleceği ve GraphQL‘in ise yeni gelecek olduğundan bahsediyor. Ben şahsen bu fikre katılmıyorum. REST ve GraphQL‘in birbirleri ile karıştırılmaması gerektiği düşüncesindeyim.

İlk olarak hem REST hem de GraphQL‘in bir arada kullanımı mümkündür. Ayrıca GraphQL doğru zaman ve doğru ihtiyaçlar karşısında kullanıldığında, veri erişimi (özellikle mobile tarafı için) ve querying konusunda client’lar için oldukça esneklik sağlamaktadır.

Peki, neden GraphQL kısmına şöyle bir giriş yapayım. Günümüz teknolojisinde mobile uygulamalarının kullanım oranının, oldukça fazla olduğu ortada. Çalışmış olduğum firmalarda, online kullanıcıların bir çoğunu neredeyse mobile kullanıcılar oluşturmakta. Development tarafına baktığımızda ise uygulamalarımızın building block’larını oluşturan API‘ları, hem web hemde mobile tarafları için kullanmaktayız.

İşte tam da bu noktada, özellikle mobile tarafı için bazı challenge’lar ile karşılaşmaktayız. Çünkü mobile tarafı için alınan data boyutu, pil ve şebeke kullanımı gibi durumlar büyük önem taşımaktadır. Genelde bu tarz challenge’ları, API‘ların önüne Gateway API‘lar yazarak aggregation işlemlerini gateway içerisinde gerçekleştirip, developer’ın istediği data’ları filtreleyerek alabilmesi ile atlatırız. Kulağa hoş gelse de, ne yazık ki iş burada bitmiyor. 🙂 Bununla birlikte versiyonlama ve backward compatibility gibi vb. challenge’larla da karşılaşmak durumunda kalabiliyoruz.

İşte bu gibi durumlar karşısında GraphQL‘in bizlere sunduğu aşağıdaki gibi bazı avantajlar bulunmaktadır.

  • Client’a ihtiyaç duyduğu data’yı, tek bir request ile kolaylıkla alabilme imkanı sunmaktadır
  • Aggregation yapılan API gateway’ler gibi tailored endpoint’lere olan ihtiyacı azaltmaktadır
  • En önemlisi de hızlı ve kolay development yapabilme imkanını sunmaktadır

Hızlı ve kolay development konusundan biraz bahsetmek gerekirse eğer, bir çok kurum içerisinde development ekipleri küçük domain’lere göre bölünüp, her ekip kendi sorumluluğunun bulunduğu uygulamaları geliştirmektedir. Benim çalıştığım bir kaç firmada bu şekildeydi.

Düşünelim, ürün detay sayfasında “1” numaralı ürünün detay bilgilerine ihtiyacımız var. Bir RESTful API içerisinde normal şartlarda bu bilgileri “api/products/1” şeklinde bir endpoint’e GET isteğinde bulunarak, kolaylıkla erişebiliriz. Mobile ekibin ise bu ürün bilgilerinden sadece “name“, “description” ve “price” field’larına ihtiyacı olup, bunlara ek olarak birde ürünün kategori bilgilerine de ihtiyacı olduğunu varsayalım.

Bu tarz durumlar karşısında, yapabileceğimiz iki farklı seçenek bulunmaktadır.

  1. İki farklı request gerçekleştirip, “roundtrips” yapmak (Söz konusu mobile side ve veri kullanımı olunca çok fazla tercih etmeyiz sanırım?)
  2. Ve ya bu işlemi tailored bir endpoint oluşturarak gerçekleştirmek

Bunlara ek olarak birde her ekibin kendi sprint’lerini koştuğunu varsayarsak, mobile ekip bu noktada “Product” API‘ından sorumlu ekibin ilgili development’ı yapmasını da bekleyecektir. Hızla yeni feature’lar eklemek istediğimiz bu teknoloji pazarında ise, beklemek çok da hoş olmayacaktır, değil mi?

GraphQL ile bir API tasarlandığında ise, mobile ekip diğer ekiplere bağımlı olmadan istediği data’yı tek bir request ile efficient bir şekilde sorgulayarak alabilmesi, hızlı bir şekilde ihtiyaçlarına göre development işlemlerini gerçekleştirebilmesi kolay bir hale gelmektedir.

Örnek bir GraphQL query’sine bakmak istersek eğer:

Yukarıdaki gibi single bir query ile istenilen data, client-driven generated bir şekilde kolaylıkla alınabilmektedir.

NOT: Bu işlemleri elbette bir RESTful API ile de yapabilmek mümkündür. (Querying, filtering, sorting, vb…) Fakat her yeni bir ihtiyaç karşısında istenilen feature’ın, RESTful API‘a implemente edilmesi veya client tarafında handle edilmesi gerekmektedir. (Roundtrips!) GraphQL bu noktada ön plana çıkarken, unutmamalıyız ki GraphQL‘in istenilenleri gerçekleştirebilmesi için ise, doğru bir mapping yapısına da sahip olması gerekmektedir.

ASP.NET Core 2.0 ile Implementasyon

Implementasyon aşamasında ise, örnek olarak basit bir “ürün” ve “kategori” içeren bir API tasarlayacağız.

Bunun için öncelikle aşağıdaki komut satırı ile yeni bir ASP.NET Core “webapi” projesi oluşturalım.

Projenin oluşturulmasından sonra “Models” isminde bir klasör oluşturarak, “Category” ve “Product” model’lerini burada tanımlayalım.

Modelleri tanımlamanın ardından “Data” isimli bir klasör daha oluşturalım. İçerisinde ise “ICategoryRepository” ve “IProductRepository” interface’lerini aşağıdaki gibi oluşturalım.

Bu interface’lerin implementasyonlarını ise, test data’ları ile birlikte aşağıdaki gibi gerçekleştirelim.

Model ve repository’ler hazır olduğuna göre, artık GraphQL‘in .NET client’ını implemente etmeye başlayabiliriz.

Bunun için öncelikle aşağıdaki komutu kullanarak, projemize GraphQL paketini NuGet üzerinden dahil edelim.

Paketi projemize ekledikten sonra, ilk olarak GraphQL Object Type‘larını tanımlamaya başlayalım.  Object type‘ları ve field’lar GraphQL schema’sının en temel bileşenlerini oluşturmaktadır. Yani, oluşturacağımız servis üzerinden hangi objeleri alabileceğimizi ve hangi field’lara sahip olduğunu belirtmektedir.

Bu ön bilginin ardından “Models” klasörü içerisinde, “CategoryType” ve “ProductType” class’larını da aşağıdaki gibi oluşturalım.

Dikkat edersek her iki class da, “ObjectGraphType” class’ından türetilmektedir. Sonrasında ise constructor içerisinden GraphQL paketinin fluent method’larını kullanarak, hangi field’lara sahip olması gerektiğini expression’lar ile tanımladık. Ardından relation field’ları için ise resolve function’larını yazdık.

Şimdi query işlemleri için kullanacak olduğumuz root type’ı oluşturacağız. Bunun için “EasyStoreQuery” isminde yeni bir class tanımlayalım ve aşağıdaki gibi kodlayalım.

EasyStoreQuery” root type’ı içerisinde, “CategoryType” ve “ProductType” da olduğu gibi constructor içerisinde schema’yı configure ettik. Configure işlemi sırasında “arguments” parametresi ile ise, ilgili field’ın hangi argument’ler doğrultusunda alınabileceğini belirttik.

NOT: “CategoryType“, “ProductType” ve “EasyStoreQuery” içerisindeki resolve function’larının source’u olarak “category” ve “product” repository’lerini configure etmek yerine, “category” ve ya “product” için olan ilgili REST endpoint’lerini de configure edebilirdik.

Artık dışarıya expose edecek olduğumuz data’mızın, structure’ını tanımlayacak olan schema’yı oluşturabiliriz. Bunun için “EasyStoreSchema” adında bir class daha oluşturalım ve “Schema” class’ından türeterek, aşağıdaki gibi kodlayalım.

Bir schema’nın sahip olabileceği iki adet temel type bulunmaktadır. Bunlardan ilki “Query”, diğeri ise “Mutation” type’ıdır. Biz ise burada sadece “Query” type’ını kullandık. “EasyStoreSchema” class’ını DI container üzerinden inject ederken, resolve type olarak ise “EasyStoreQuery” type’ını parametre olarak geçeceğiz.

Artık geriye yapmamız gereken iki şey kaldı. Bunlardan ilki GraphQL endpoint’ini hazırlamak, bir diğeri de service injection işlemlerini gerçekleştirmek. Endpoint’i hazırlamak için öncelikle “Models” klasörü içerisinde “GraphQLQuery” isminde bir request class’ı oluşturalım ve aşağıdaki gibi tanımlayalım.

Request objesini tanımlamanın ardından, “Controllers” klasörü içerisinde ise “GraphQLController” isminde bir controller class’ı ekleyerek kodlamaya başlayalım.

İlk olarak “Route” attribute’ü ile endpoint’i belirledik ve ardından “IDocumentExecuter” ve “ISchema” interface’lerinin inject işlemlerini gerçekleştirdik. Bu noktada “IDocumentExecuter“, “ExecutionOptions” parametresini execute ederek, client’ın istemiş olduğu data’yı oluşturacaktır. Bu noktada, client-driven application development yapabilmek, çok da hoş değil mi?

Artık geriye sadece dependency injection işlemi kaldı. “Startup” class’ı içerisinde injection işlemlerini aşağıdaki gibi gerçekleştirelim.

İşte hepsi bu kadar.

Şimdi terminal üzerinden “dotnet run” komutunu çalıştırarak, bir kaç test yapabiliriz.

NOT: Ben test işlemleri için “Postman” kullanacağım.

Aşağıdaki sorguyu GraphQL endpoint’ine, POST olarak gönderelim ve sonucunu görelim.

Response’a baktığımızda ise aşağıda gördüğümüz gibi, “1” numaralı kategoriye ait “id” ve “name” field’ları gelmiştir.

Peki ya bu kategoriye ait ürünleri, “id“, “name“, ve “price” field’ları ile gelmesini de istiyorsak, bu durumda ne olacak? Gayet basit. Tek yapmamız gereken, sorgumuzu aşağıdaki gibi güncellemek olacaktır.

Sonucu ise aşağıdaki gibi ortada.

Sonuç

GraphQL ile PoC yaparken gerçekten çok keyif aldım. Bu konudaki benim fikrim ise, eğer database schema’mız ve ya API design’larımız resource olarak kullanılmaya uygun ise, GraphQL’i implemente etmek özellikle client-driven application development için doğru bir tercih olacaktır. Ayrıca GraphQL client’a sadece istediği ölçüde data’yı verebilmesiyle beraber, server ile client arasındaki veri boyutunun küçülmesini de sağlamaktadır.

Örnek projeye ise buradan erişebilirsiniz: https://github.com/GokGokalp/ASP.NET-Core-2.0-GraphQL-Sample

Referanslar

https://github.com/graphql-dotnet/graphql-dotnet

http://graphql.org/learn/

Bu makale toplam (8210) kez okunmuştur.

34
0



Kategori: ASP.NET Core GraphQL

13 Yorum

  1. Kerim Kerim

    Güzel paylaştım. Teşekkürler

  2. Hüseyin Hüseyin

    Paylaşımız için teşekkür ederim ama kafamda oturmayan şeyler var 🙂
    GraphQL kullanarak web service üzerinde herhangi bir değişiklik yapmadan hızlıca bir query oluşturup istediğimiz columnları tanımlayarak data getirme işlemleri yapabiliyoruz, bu işlem bize ortam bazında avantajlar sağlıyor(response data size or extra columns).

    Peki burada bir spesifik bir filter vermek istediğimizde web service burada nasıl davranıyor. Örneğin şurada bir örnek var; https://www.howtographql.com/graphql-js/10-filtering/
    Queryde belirttiğimiz filter tanımları bir repository aracılığıyla db seviyesindemi filter yapıyor yoksa repositoryde örneğin GetProducts() methodundan dönen liste içerisindemi bir filter uyguluyor?

    Bu konuda biraz detay verebilir misiniz? Artı ve eksileri varmıdır?

    • Merhaba, teşekkür ederim öncelikle yorumunuz için. Bu işlemler aslında sizin ilgili source’u ve query argument’ları nasıl tanımladığınıza göre biraz değişiklik gösteriyor. Hatta relation’ların resolve kısımlarına yönelik bunun üzerine tartışılmış bir çok N+1 query sorusu, query optimizasyon vb. gibi çözümleri de mevcut. (Benim gördüğüm) Benim yapmış olduğum basit örnekte “id” argument’ı ile ilerlediğim için bir filtering söz konusu değil db tarafında, in-memory gerçekleşmektedir. Buradaki query argument’larını çoğaltabilir ve ya kendi query argument’larımız için graph type’lar oluşturabilir ve spesifik filter query’leri geçebilerek, predicate’ler ile de bir sorgu oluşturabiliriz db’ye hit etmeden önce veya ilgili servis’in bir filter endpoint’i varsa. Bu kullanmış olduğum .NET client’ı hala geliştirilmekte olan bir paket, ayrıca benim için de baya yeni bir konu süredir üzerinde çalışıyorum bu tarz concern’lere karşı, deneyimlerimi buradan paylaşmaya devam edeceğim.

      • Hüseyin Hüseyin

        Terimler arasında kaybolup gittim ama konuyla ilgili yeni makalelerinizi merakla bekliyorum 🙂
        Ama özetle istemciden sunucudaki ilgili endpointe gelen request içerisindeki filtreyi bir convert işlemiyle ilgili orm tool yada data çekmek için kullandığımız data toolu ne ise, onun filter yapısına uydurup data çekmek işi çözecektir diye anlıyorum. Tabi bu convert işlemindeki pradicateleri uyumlu bir şekilde otomatik çeviren bir modül yoksa durum biraz vahim olabilir.

        • Merhaba tekrar, evet. 🙂 Örnek vermek gerekirse eğer: “EasyStoreQuery” içerisindeki argument’lere bakarsak, “new QueryArgument {Name = “id”, Description = “Category id”}” şeklinde bir id parametresi ekliyoruz aslında. GraphQL’in .NET client’ı ile buraları şekillendirmek bize kalıyor biraz. 🙂 Bende üzerinde çalışmaktayım halen, ilerleyen bölümlerde paylaşmaya devam edeceğim edinebildiğim farklı tecrübeleri. 🙂

  3. son son

    Paylaşım için teşekkürler güzel açıklama. Ben yinede grapql in neden kullanılması gerektiğini tam anlayamadım. Linq le çekip filtring yapmamızdan bir farkı yok gibi görünnüyor. Belki burda linq expressioni direk mobilden geliyomuş gibi düşünebiliriz. Buda mobilci için backend geliştirme beklemesini azaltır gibi.

  4. praneet praneet

    Thanks ! Great post. Exactly what I was looking for.

  5. Gin Gin

    So how to return a list Products?

    • Hi Gin, What you mean by “a list Products”? We already return product list with the following query:

      {
      “query”:
      “query{
      category(id:1){
      id
      name
      products{
      id
      name
      price
      }
      }
      }”
      }

      If you want to return a product list without category, you should implement it in “EasyStoreQuery” query class without “id” parameter. Then you can be querying.

  6. Paul Paul

    Great write up and project. Thank you for your time explaining this.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*