GraphQL Nedir, .Net Core GraphQL Kullanımı

GraphQL, Api'larınız için server-side runtime execute edebileceğiniz bir query language'dir. Dil ve teknoloji bağımsız olarak bir çok framework çin desteği bulunmaktadır (asp.net, java, nodejs etc.) ve herhangi bir database yada farklı data-storage engine'lara bağımlı olmaksızın entegre edilebilir bir yapıdır. Klasik api'lardan farklı olarak client retriew etmek istediği alanları ve uygulanacak filtreleri kolayca belirtebilir.

Örnek verecek olursak; userList dönen bir api tasarladınız ve geriye db'de bulunan user'ları belirli filtreler uygulayarak return ediyor. Api v1.0'da request olarak userId ve response olarakda userFullName ve birthDate dönen bir dto olarak tasarlandı. v2.0'da dedilerki request'e birthdate ve response'a da zipcode alanları eklenecek. Yani tekrardan bir geliştirme yapmanız gerekmekte. Bir kaç hafta sonra dedilerki başka bir ekip daha bu endpoint'i kullanacak ancak onlar şu-şu parametrelere göre kullanmak istiyor vs. GraphQl sağladığı fluent yapı ile birlikte isteyene istediğini gibi filtreleyebildiği ve entity'nize göre response oluşturabildiği bir yapı tasarlamayı sağlamakta. 

Örnek bir proje üzerinden anlatmaya devam edelim.

Creating an Asp.Net Core Web Application 

İlk olarak vs'da GraphQL_Sample adında bir Asp.net Core WebApp projesi oluşturalım. En son güncel olan .net core versiyonu kurulu olduğundan versiyon 3.1 ile uygulamayı oluşturdum.

Sonrasında nuget üzerinden GraphQL'in son versiyonu olan 2.4.0 paketini projemize install edelim. Dilerseniz console üzerindende install komutunu çalıştırabilirsiniz.

Install-Package GraphQL -Version 2.4.0

 

Öncelikle projemizde kullanacağımız data-source entity tanımlamalarını yapalım. Projemiz herhangi bir data-soruce'da bulunan Customer tablosundaki kayıtları dönen bir api olsun ve bu api'a ait Customer entity'sini aşağıdaki gibi oluşturalım.

Define Entity and Repository

public class Customer
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string ZipCode { get; set; }
    public bool IsActive { get; set; }
}

Bu entity'ye ait CustomerRepository ve ICustomerRepository class'larını oluşturup bağımlılığı asp.net core'un default DI tool'una Startup.cs içerisinde register edelim

public interface ICustomerRepository
{
    List<Customer> All();
}
public class CustomerRepository : ICustomerRepository
{
    public List<Customer> All()
    {
        return new List<Customer>
        {
            new Customer
            {
                Id = new Guid(),
                FirstName = "Brain",
                LastName = "Adams",
                BirthDate = new DateTime(1985,11,20),
                IsActive = true,
                ZipCode = "11572"
            },
            new Customer
            {
                Id = new Guid(),
                FirstName = "Joe",
                LastName = "Colmun",
                BirthDate = new DateTime(1991,1,14),
                IsActive = true,
                ZipCode = "22687"
            },
            new Customer
            {
                Id = new Guid(),
                FirstName = "Lorena",
                LastName = "McCarty",
                BirthDate = new DateTime(1972,7,4),
                IsActive = true,
                ZipCode = "11572"
            },
            new Customer
            {
                Id = new Guid(),
                FirstName = "Ivan",
                LastName = "Lopez",
                BirthDate = new DateTime(1990,2,9),
                IsActive = true,
                ZipCode = "56874"
            },
            new Customer
            {
                Id = new Guid(),
                FirstName = "Jason",
                LastName = "Smith",
                BirthDate = new DateTime(200,8,17),
                IsActive = true,
                ZipCode = "96314"
            },
            new Customer
            {
                Id = new Guid(),
                FirstName = "Clain",
                LastName = "Adams",
                BirthDate = new DateTime(1986,5,5),
                IsActive = true,
                ZipCode = "11572"
            }
        };
    }
}

All() metodu hayali db-storage'da bulunan Customer tablosundaki bütün kayıtları select eden metod olarak düşünebiliriz ve yukarıdaki gibi dummy olarak bir customerList return etmekte.
ICustomerRepository ve CustomerRepository'ye ait dependency injection işlemini aşağıdaki gibi .net core default DI tool'unda tanımlayalım. Bunun için Startup.cs içerisinde bulunan ConfigureServices emtodunu kullanacağız.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ICustomerRepository, CustomerRepository>();
}

Schema;
Schema, GraphQL API'sinin ana yapı taşlarından biridir.
Schema çoğunlukla Query, Mutation, Types bilgilerini içerir. Şemadaki sorgu bir ObjectGraphType'tır.

Şimdi sırada GraphQL type'larını oluşturma var. GraphQL doğrudan bizim poco sınıflarımızı alıp mapping işlemi yapamıyor. Bunun için CustomerType adında ObjectGraphType sınıfını inherit alan bir mapping sınıfı oluşturmamız gerekmekte.

public class CustomerType : ObjectGraphType<Customer>
{
    public CustomerType()
    {
        Field(x => x.Id, type: typeof(IdGraphType)).Description("Id of the Customer");
        Field(x => x.FirstName);
        Field(x => x.LastName).Description("Customer's lastName");
        Field(x => x.BirthDate);
        Field(x => x.IsActive);
        Field(x => x.ZipCode);
    }
}

Yukarıda görüldüğü üzre Customer sınıfı için graphQl da mapping'i bulunan bir sınıf yarattık ve constructor içerisinde bu sınıfa ait field'ların mapping'ini tanımladık ve yine Startup.cs içerisinde container'a register edelim.

services.AddSingleton<CustomerType>();

Sonraki adım ise AppQuery sınıfını oluşturmak. Bu sınıf query'ler oluşturmamıza yardımcı olacak. GraphQL de "customers" key'ine karşılık bütün müşterileri return eden bir tanım yaptık

public class AppQuery : ObjectGraphType
{
    public AppQuery (ICustomerRepository customerRepository)
    {
        Field<ListGraphType<CustomerType>>("customers", resolve: context => customerRepository.All());
    }
}

RotQuery sınıfını yine Startup.cs içerisinde DI tool'una register edelim.

services.AddScoped<AppQuery>();

AppSchema sınıfı ile GraphQL örneğimiz için kullanacağımız schema anımlamasını yapalım.

public class AppSchema : Schema, ISchema
{
    public AppSchema(IDependencyResolver resolver) : base(resolver)
    {
        Query = resolver.Resolve<AppQuery>();
    }
}

GraphQL NuGet kütüphanesi, build-in DI tool'u ile birlikte gelir. 'Schema' sınıfı 'GraphQL.IDependencyResolver' property'sine sahiptir ve böylece gereken tüm ObjectGraphType'ı ihtiyaç durumunda koalyca resolve edebilir.

Yine bu yukarıda tanımladığımız Schema için DI registration'ınını Startup.cs içinde tanımlayalım.

services.AddScoped<IDependencyResolver>(_ => new FuncDependencyResolver(_.GetRequiredService));
services.AddScoped<ISchema, AppSchema>();

Son olarak ise Api'a ait olan CustomerController içerisinde ilgili endpoint'i aşağıdaki gibi oluşturalım.

[ApiController]
[Route("[controller]")]
public class CustomerController : ControllerBase
{
    private readonly ISchema _schema;
    private readonly IDocumentExecuter _executer;

    public CustomerController(ISchema schema, IDocumentExecuter executer)
    {
        _schema = schema;
        _executer = executer;

    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody] CustomerQueryDto query)
    {
        var result = await _executer.ExecuteAsync(_ =>
        {
            _.Schema = _schema;
            _.Query = query.Query;
        }).ConfigureAwait(false);
		
		if (result.Errors?.Count > 0)
        {
            return BadRequest(result);
        }
 
        return Ok(result.Data);
    }
}

Artık api'ımızı test edebiliriz. Postman üzerinden aşağıdaki gibi ilk olarak RoorQuery de belirttiğimiz All() metodu için tanımladığımız query için spesific field'lar için bir request atalım ve response'da istekte bulunduğumuz alanların geldiğini görelim.

{ "query": "query { customers { id firstName } }" }

Farklı query'ler yazacak olursak; birkaç parametreye göre filtre uygulayıp sonuçları dönen bir query için aşağıdaki gibi appQuery sınıfının içerisinde field tanımını yapalım.

Field<ListGraphType<CustomerType>>("filterCustomers",
    arguments: new QueryArguments
    {
        new  QueryArgument<StringGraphType> { Name = "firstName"},
        new  QueryArgument<StringGraphType> { Name = "lastName"},
        new  QueryArgument<BooleanGraphType> { Name = "isActive"},
        new  QueryArgument<DateGraphType> { Name = "birthDate"},

    },
    resolve: context =>
      {
          var firstName = context.GetArgument<string>("firstName");
          var lastName = context.GetArgument<string>("lastName");
          var isActive = context.GetArgument<bool>("isActive");

          return customerRepository.All().Where(x => x.FirstName.ToLower() == firstName.ToLower() && 
                                                     x.LastName.ToLower() == lastName.ToLower() && 
                                                     x.IsActive == isActive).ToList();
      });

Tekrardan postman üzerinden isteği attığımızda response'un döndüğünü görebiliriz.

{ "query": "query { filterCustomers(firstName:\"Lorena\",lastName:\"McCarty\", isActive:true) { id firstName lastName } }"}

 

Aliases
Api tarafında filteredCustomers şeklinde bir tanımlama yaptığımızdan requestte bulunurkende aynı fieldName'de dönmekte. Ancak aliases kullanarak response field'ı modify edebiliriz. Kullanım şekli olarak aşağıdaki gibidir ve graphql api'nize bu isteği attığınızda artık response'da bulunan array'in adı customers olacaktır.

query {
 customers: filteredCustomers(firstName: "Lorena") {
  id
  firstName
 }
}

Fragments

GraphQL API'sindeki iki kayıt arasında karşılaştırma yapmak için kullanılır. Aşağıdaki sorgu örneğindeki gibi bir fragment query'si oluşturabiliriz.

query {
 customerOne: filterCustomers(firstName: "Lorena") {
  ...props
 }
 customerTwo: filterCustomers(firstName: "Clain") {
  ...props
 }
}
fragment props on CustomerType {
 id
 firstName
 lastName
}

 

Özetleyecek olursak; qraphQl'e başlangıç seviyesinde değinmiş olmakla beraber .net core ile oldukça basit bir şekilde GraphQl tabanlı api'lar tasarlayabilir ve ihtiyacınıza göre complex query tanımlamaları yapabilirsiniz.

Source

Add comment