.NET Core GraphQL

.NET Core ile GraphQL tabanlı web servis için gerekli olan paketlerin kurulumu, kullanımı ve GraphQL web servis oluşturma işlemi ile ilgili bilgiler yer alıyor.

GraphQL nedir?

GraphQL hakkında detaylı bilgi için GraphQL Nedir? yazıma bakabilirsin.

GraphQL kurulumu

İlk olarak .NET Core CLI ile web projesi oluşturalım.

dotnet new web -o GraphQLProjesi

Gerekli olan paketleri yükleyelim.

dotnet add package GraphQL

Paket şema(schema), sorgu(query) ve veri yapısı(type) oluşturmak için kullanılır.

dotnet add package GraphQL.Server.Transports.AspNetCore

Paket GraphQL şemasını .NET Core projesine middleware (orta-katman) olarak eklemek için kullanılır.

dotnet add package GraphQL.Server.Ui.Playground

Paket GraphQL sorgulama editörü için kullanılır.

NOT: GraphQL editör paketi GraphQL web servisini ek bir yazılıma ihtiyaç duymadan esnek bir şekilde sorgulamayı sağlar.

NOT: Visual Studio kullanılıyorsa Package Manager Console alanına dotnet add package yerine Install-Package yazılır.

GraphQL web servis oluşturma

GraphQL web servisi oluşturmak için ilk olarak bir şema belirlememiz gerekir.

Schema – Şema

Şema içerisinde Query, Mutation, Subscription yapıları yer alır.

public class AppSchema : GraphQL.Types.Schema
{
    public AppSchema(GraphQL.IDependencyResolver resolver) : base(resolver)
    {
        Query = resolver.Resolve<RootQuery>();
    }
}

Şema GraphQL.Types.Schema sınıfından kalıtım alınarak yapılır.

Şema içerisinde yer alan Query, Mutation ve Subscription özelliklerine oluşturulan GraphQL sorguları(query), işlemleri(mutation) eklenir.

Yukarıda RootQuery sorgusunu şemaya eklemiş olduk.

Query – Sorgulama

GraphQL sorgularında yer alan alanlar ObjectGraphType sınıfındaki Field metodu ile belirlenir.

public class RootQuery : GraphQL.Types.ObjectGraphType
{
    public RootQuery()
    {
        Name = "RootQuery";
        Description = "Burada Query açıklaması yer alır.";

        Field<StringGraphType>(
            name: "mesaj",
            description: "Burada alan açıklaması yer alır.",
            resolve: context => "Merhaba GraphQL!");
    }
}

Alana ait veri yapısı için ön tanımlı veri yapıları kullanılabilir.

  • StringGraphType
  • IntGraphType
  • FloatGraphType
  • BooleanGraphType
  • IdGraphType
  • DateGraphType
  • ListGraphType

Ayrıca ObjectGraphType sınıfı kullanılarak özel bir veri yapısı tanımlanabilir.

Field metodu sorguda yer alacak alan adını name, açıklamasını description ile belirler.

Resolve parametresi ise sorgu sonucu gönderilecek veriyi belirtir.

Resolve parametresi bir fonksiyon aldığından gönderilecek veri dosyadan, veritabanından alınarak fonksiyon dönüş değeri olarak gönderilir.

Şema ve sorgu hazırlandıktan sonra Startup dosyasına GraphQL şeması servis olarak eklenir.

class Startup {

    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        // Gerekli olan bağımlılıklar eklenir.
        services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
        services.AddScoped<AppSchema>();

        // GraphQL servisi dahil edilir.
        services.AddGraphQL()
                .AddGraphTypes(ServiceLifetime.Scoped);
        // ...
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // ...
        // GraphQL servisine oluşturulan şema gönderilir.
        app.UseGraphQL<AppSchema>();
        // GraphQL editörü eklenir.
        app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions());
        // ...
    }
}

Ayarları yaptıktan sonra projeyi çalıştıralım.

dotnet run

veya

dotnet watch run

Proje çalıştırıldıktan sonra /ui/playground adresi ile GraphQL editörüne ulaşabiliriz.

Editörün sağ kısmında yer alan Schema ile şema bilgilerine, Docs ile Field metodu ile eklenen alanlara ve açıklamalarına ulaşabiliriz.

Editörün sol kısmında yer alan alana aşağıdaki sorguyu yazıp çalıştıralım.

{
  mesaj
}

veya

query IlkSorgu {
  mesaj
}

Sorgu çalıştırıldıktan sonra sağ kısımda aşağıdaki gibi bir sonuç görünecektir.

{
  "data": {
    "mesaj": "Merhaba GraphQL!"
  }
}

RootQuery sorgusuna yeni bir alan ekleyelim.

Field<NonNullGraphType<IntGraphType>>(
    name: "sayi",
    description: "Her bir istek sonucunda rastgele bir sayı üretecektir.",
    resolve: context => new Random().Next(0, 100));

Yukarıda yer alan NonNullGraphType ile sayi alanının null olmayacağını IntGraphType ile sonucun sayısal bir değer olacağını belirttik.

Sorgu sonucunu mesaj alanından farklı olarak Random sınıfı ile dinamik olarak oluşturduk.

GraphQL editörüne aşağıdaki sorguyu yazıp çalıştıralım.

query IkinciSorgu {
  sayi
}

mesaj alanını da almak için sadece mesaj alanını eklemek yeterli olacaktır.

query IkinciSorgu {
  sayi
  mesaj
}

Type – Veri yapısı

.NET ve Java gibi platformların en önemli özelliği düzenli-mimari bir geliştirme yapmaya imkan vermesidir.

MVC içerisinde yer alan Model, Katmanlı mimaride kullanılan Data, Repository, Entities gibi kısımlarda uygulamaya ait veri yapısı-modeli belirlenir.

public class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string FullName => $"{FirstName} {LastName}";
}

Bu veri yapısını-modelini kullanarak GraphQL veri yapısı oluşturmak için ObjectGraphType sınıfına generic olarak geçmek yeterli olacaktır.

public class PersonType : GraphQL.Types.ObjectGraphType<Models.Person>
{
    public PersonType()
    {
        Field(p => p.ID).Description("Person ID");
        Field(p => p.FirstName).Description("Person FirstName");
        Field(p => p.LastName).Description("Person LastName");
        Field(p => p.Email).Description("Person Email");
    }
}

Veri yapısını kullanmak için RootQuery sorgusuna yeni bir alan ekleyelim.

Field<ListGraphType<PersonType>>(
    name: "people",
    resolve: context => new List<Models.Person>()
    {
        new Models.Person{ID=1, FirstName="Yusuf", LastName="SEZER"},
        new Models.Person{ID=2, FirstName="Ramazan", LastName="SEZER"},
        new Models.Person{ID=3, FirstName="Sinan", LastName="SEZER"},
        new Models.Person{ID=4, FirstName="Mehmet", LastName="SEZER"}
    });

Birden fazla değer olacağı için ListGraphType ve oluşturduğumuz veri yapısını(PersonType) kullandığımıza dikkat edin.

NOT: Gönderilecek değerler dosyadan, veritabanından veya başka bir kaynaktan alınabilir.

GraphQL editörüne aşağıdaki sorguyu yazıp çalıştıralım.

query UcuncuSorgu {
  sayi
  people {
    firstName
    lastName
  }
}

Sorgu sonucunda sayi ve dizi halinde firstName, lastName değerleri listelenecektir.

Arguments – Argümanlar

GraphQL web servisine parametre göndermek için Arguments yapısı kullanılır.

Arguments yapısını kullanmak için Field metodunun arguments değerine gerekli argümanları yazmak yeterli olacaktır.

Argüman değeri context.GetArgument ile alınarak işlem yapılır.

Field<PersonType>(
    name: "person",
    arguments: new QueryArguments
    (
        new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "id" }
    ),
    resolve: context =>
    {
        var id = context.GetArgument<int>("id");
        List<Models.Person> people = new List<Models.Person>() {
            new Models.Person{ID=1, FirstName="Yusuf", LastName="SEZER"},
            new Models.Person{ID=2, FirstName="Ramazan", LastName="SEZER"},
            new Models.Person{ID=3, FirstName="Sinan", LastName="SEZER"},
            new Models.Person{ID=4, FirstName="Mehmet", LastName="SEZER"}
        };

        Models.Person foundPerson = people.Find(p => p.ID == id);

        if (foundPerson == null)
        {
            context.Errors.Add(new GraphQL.ExecutionError("Person not found!"));
            return null;
        }

        return foundPerson;
    });

Örnekte Field metodu arguments ile argümanları belirledik ve parametre değerini resolve içerisinde context.GetArgument ile alarak arama işlemi yaptık.

Parametreye karşılık gelen bir değer bulunmadığı durumda context.Errors ile hata mesajı gönderdik.

Parametreye karşılık gelen bir değer bulunduğunda ise bulunan değeri gönderdik.

NOT: Tek değer gönderileceği için alan veri türü olarak sadece PersonType kullanıldığına dikkat edin.

GraphQL editörüne aşağıdaki sorguyu yazıp çalıştıralım.

query DorduncuSorgu {
  sayi
  person(id: 1) {
    firstName
    lastName
  }
}

Parametre olarak olmayan bir değer yazıldığında context.Errors alanına eklenen hata gönderilecektir.

Mutations – İşlemler

Veri ekleme, silme ve güncelleme gibi işlemleri yapmak için Mutations yapısı kullanılır.

Mutations yapısı Query ve Arguments yapılarının bir benzeridir.

Mutations tanımlama işlemi Query tanımlama işleminde olduğu gibi aşağıdaki gibi yapılır.

public class RootMutation : GraphQL.Types.ObjectGraphType
{
    public RootMutation()
    {
        Name = "RootMutation";
        Description = "Burada Mutation açıklaması yer alır.";
    }
}

Mutation şemaya eklenir.

public class AppSchema : GraphQL.Types.Schema
{
    public AppSchema(GraphQL.IDependencyResolver resolver) : base(resolver)
    {
        Query = resolver.Resolve<RootQuery>();
        Mutation = resolver.Resolve<RootMutation>();
    }
}

Mutation işlemleri Query ve Arguments yapısındaki gibi yapılır.

Field<StringGraphType>(
    name: "addPerson",
    description: "Add new person.",
    arguments: new QueryArguments(
        new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "firstName" },
        new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "lastName" }
    ),
    resolve: context =>
    {
        var firstName = context.GetArgument<string>("firstName");
        var lastName = context.GetArgument<string>("lastName");

        System.IO.File.AppendAllText("person.txt", $"{firstName} {lastName} {System.Environment.NewLine}");

        return "Başarılı";
    });

Mutation yapısının kullanımı aşağıdaki gibidir.

mutation KisiEkle {
  addPerson(firstName: "Yusuf", lastName: "SEZER")
}

İşlem sonrası person.txt dosyasına parametre olarak gönderilen değerler eklenecektir.

Mutations yapısındaki parametreleri ayrı ayrı belirlemek yerine aşağıdaki gibi yeni bir GraphQL veri yapısı oluşturulabilir.

public class PersonInputType : GraphQL.Types.InputObjectGraphType<Models.Person>
{
    public PersonInputType()
    {
        Field(p => p.FirstName);
        Field(p => p.LastName);
    }
}

NOT: Argümana ait veri yapısı için InputObjectGraphType sınıfının kullanıldığına dikkat edin.

Oluşturulan veri yapısı aşağıdaki gibi kullanılır.

Field<StringGraphType>(
    name: "addPerson",
    description: "Add new person.",
    arguments: new QueryArguments(
        new QueryArgument<NonNullGraphType<PersonInputType>> { Name = "person" }
    ),
    resolve: context =>
    {
        var person = context.GetArgument<Person>("person");

        System.IO.File.AppendAllText("person.txt", $"{person.FirstName} {person.LastName} {System.Environment.NewLine}");

        return "Başarılı";
    });

Mutations yapısının kullanımı aşağıdaki gibidir.

mutation KisiEkle2 {
  addPerson(person: { firstName: "Yusuf", lastName: "SEZER" })
}

GraphQL web servisini kullanmak için GraphQL adresine aşağıdaki gibi bir istek göndermek yeterli olacaktır.

sunucu-adresi/graphql?query={mesaj}

Ancak Apollo Client, Relay gibi çeşitli araçları kullanmak daha esnek sorgular için faydalı olacaktır.

GraphQL Aliases, Fragments, Named Queries, Variables, Directives, Interfaces, Unions gibi özelliklere sahiptir.

Özellikler hakkında detaylı bilgi için aşağıdaki GraphQL sayfasına bakabilirsin.

https://graphql.org/learn

.NET Derslerine buradan ulaşabilirsiniz.

Web Servis Derslerine buradan ulaşabilirsiniz…

Hayırlı günler dilerim.

Yusuf SEZER

Yusuf SEZER

Computer Engineer who interested about web technologies, algorithms, artificial intelligence and embedded systems; constantly exploring new technologies.


Bunlara'da bakmalısın!