.Net core da GraphQL’de değişim(mutation)

Birkan Tuğcu
4 min readSep 30, 2019

Merhaba bu yazıda bir önceki yazımda bahsettiğim mutation işlemlerini yani GraphQL ile nasıl değişken(variable) gönderilebileceğini ve kayıt etme ,değiştirme ile silme gibi işlemleri nasıl yapabileceğinizi anlatmaya çalışıcam. İlk defa GraphQL ile ilgili işlem yapıyorsanız aşağıdaki yazı size faydalı olabilir.

GraphQL’in sitesinde ki ilk tanım :

A query language for your API

GraphQL bir query olmasına rağmen data değiştirme işlemleri içinde kullanılabilir. Bu tarz işlemler için mutation diye bir yapı kurulmuş. Aslında query ler ile de değişkenleri alıp gerekli işlemler yapılabilir. Ancak bu yapı sayesinde query ve commandları birbirinden ayırabiliyoruz. REST yapılarda nasıl get ile post ayrı ise burada da ayırmamızda fayda var diye düşünüyorum.

İlk olarak mevcut projenin linki:

Projede mutation adında yeni bir branch açarak devam ediyorum. Bu branch ta GraphQLController ‘a gidip Post actionResult fonksiyonunu kopyalayıp yapıştırıyorum. Adını Get ve tipini de HttpGet olarak değiştiriyorum. İsterseniz bu hali ile Postman ile Get tipinde istek atarak çalıştığını görebilirsiniz. Post fonksiyonunun hazır halini aşağıda görebilirsiniz üstünden anlatmak daha iyi olur diye düşündüm.

[HttpPost]
public async Task<IActionResult> Post([FromBody]GraphQLQuery query)
{
if (query == null) { throw new ArgumentNullException(nameof(query));
}
var executionOptions = new ExecutionOptions { Schema = _schema, Query = query.Query , Inputs = query.Variables.ToInputs() };try
{
var result = await _documentExecuter.ExecuteAsync(executionOptions).ConfigureAwait(false);
if (result.Errors?.Count > 0)
{
return BadRequest(result);
}
return Ok(result);
}
catch (Exception ex)
{
return BadRequest(ex);
}
}

Değişen tek satır

var executionOptions = new ExecutionOptions { Schema = _schema, Query = query.Query , Inputs = query.Variables.ToInputs() };

Artık mutation işlemlerinde nesne göndermek için variables kullanacağımız için daha önceden tanımladığımız GraphQLQuery nesnesi içindeki Variables alanını GraphQL ‘e input olarak veriyoruz. Bu bize istek oluştururken çok değişkenli nesneleri daha kolay oluşturmamızda yardımcı oluyor. Controller şu anda hazır.

Önceki yazıda query için aşağıdaki argument yapısını kullanmıştık.

arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IntGraphType>> { Name = “id”, Description = “Category id” }
)

Bu şekilde birden fazla argument tanımlayarak gelecek değişkenleri alabiliriz. String değerler için StringGraphType, Int değerler için IntGraphType vs. kullanabiliriz. Ama ben kendi nesnemi oluşturmak istedim. Onu da ProductInputType adında InputObjectGraphType’tan türeterek aşağıdaki şekilde oluşturdum. Bu sayede birden çok değişkeni tek nesnede yönetebilirim.

public class ProductInputType : InputObjectGraphType
{
public ProductInputType()
{
Name = "productInput";
Field<NonNullGraphType<StringGraphType>>("name");
Field<NonNullGraphType<FloatGraphType>>("price");
Field<StringGraphType>("description");
Field<NonNullGraphType<IntGraphType>>("categoryId");
}
}

Bu yapıda Name bizim mutation isteği gönderirken kullanacağımız tipin adı. Postmandan deneme yaparken daha iyi anlayacaksınız. Aynı ProductType içinde olduğu gibi burada da alanları(field) tanımlayıp değişken tiplerini belirliyoruz. NonNullGraphType ile de null olmasını istemediğimiz alanları belirtiyoruz. Bu sayede istek fonksiyona gelmeden alan kontrolü sağlanmış oluyor.

Şimdi mutation işlemlerinin yapılacağı sınıfları hazırlayalım. Models klasörü içindeki GraphQLModels klasörü içine AppMutation adında dosya ekledim ve aşağıdaki gibi hazırladım.

public class AppMutation : ObjectGraphType
{
public AppMutation(IProductRepository productRepository)
{
Field<ProductType>("createProduct",arguments: new QueryArguments(new QueryArgument<NonNullGraphType<ProductInputType>> { Name = "product"
}),resolve: context =>{
var product = context.GetArgument<Product>("product");
return productRepository.Add(product);
}
);
}
}

Burada dikkat ederseniz query fonksiyonlarının tanımlaması ile aynı. Sadece aşağıdaki kod parçasında olduğu gibi kendi değişkenimizi tanımladık. Bu sayede nesne şeklinde değişkenlerimizi alabileceğiz.

new QueryArguments(new QueryArgument<NonNullGraphType<ProductInputType>>    { Name = "product"
})

GraphQL ‘e bu fonksiyonun mutation olduğunu belirtmek için AppSchema sınıfımız içinde ki yoruma alınmış Mutation sınıfımızı açıyoruz. Son hali aşağıdaki gibi olacak.

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

GraphQL gelen istek query ise AppQuery içindeki fonksiyonları, mutation ise AppMutation içindeki fonksiyonları çalıştıracak.

Startup.cs dosyamıza bu eklediğimiz sınıfları eklememiz gerekiyor. Önceki örnek içinde bu kodlar olduğundan yorumda olan satırları açtığımızda herşey hazır olacak. Startup dosyamızın da son hali aşağıdaki gibi olmalı.

services.AddScoped<AppQuery>();
services.AddSingleton<ICategoryRepository, CategoryRepository>();
services.AddSingleton<IProductRepository, ProductRepository>();
services.AddTransient<CategoryType>();
services.AddTransient<ProductType>();
services.AddScoped<AppMutation>(); //Eklenen satırlar
services.AddScoped<ProductInputType>(); //Eklenen satırlar
services.AddScoped<AppSchema>();
services.AddScoped<IDocumentExecuter, DocumentExecuter>();
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));var sp = services.BuildServiceProvider();services.AddScoped<ISchema>(_ => new AppSchema(sp.GetRequiredService<IDependencyResolver>()));

Postman dan Post işlemimizi denerken isteği aşağıdaki gibi hazırlıyoruz.

{ 
"query":"mutation($product: productInput!){createProduct(product:$product){name}}",
"variables":"{product:{name: 'deneme2', categoryId: 1,price: 100}}"
}

Dikkat ederseniz query içinde artık query değil mutation isteği bulunuyor. Bu yapı variable kullanmak için GraphQL tarafından hazırlanmış. Mutation işlemi yapıldığı zaman input değeri olarak eklediğimiz değişken nesnesi fonksiyonda product nesnesine dönüştürülüyor. Mutation tanımlamasında daha önce ProductInputType tanımlarken kullandığımız name değişkenini veriyoruz. GraphQL bu sayede o değişkene erişebiliyor.

mutation($product: productInput!)

Aşağıdaki kısma dikkat ederseniz query den hiç farklı değil. Bu işlemden sonra dönmesini istediğiniz alanları tanımlayabiliyorsunuz. Ben id ile name dönmesini istemişim.

createProduct(product:$product){id name}

Bu işlem sonrasında bize dönecek olan datamız aşağıdaki gibi olmalı.

{
"data":
{
"createProduct":
{
"id": 4,
"name": "deneme2"
}
}
}

Seed olarak listede 3 nesnemiz olduğundan id olarak 4 ve bizim gönderdiğimiz name alanını geri döndü. Bu konuyla alakalı detaylı bilgiyi aşağıdaki linkten bulabilirsiniz.

Mutation yapısına odaklandığım için sadece ekleme ile ilgili açıklama yaptım ama geri kalan işlemleri benzer şekilde ekleyebilirsiniz.

Sonuç olarak, bence kullanımı gayet zevkli bir yapı. Onlarca endpoint oluşturmak ve hepsini yönetmek projeler büyüdükçe sorun olarak karşımıza çıkmıyor değil. Mutation sayesinde oluşturma yada silme işlemlerinde de istenen dataların geri dönülebilmesi çok büyük rahatlık. İlerleyen günlerde GraphQL’in kendi cache mekanizması ile ilgili bir çalışma daha yapmayı düşünüyorum. Okuduğunuz için teşekkür ederim.

--

--