1923

都内と業界の隅っこで生活しているエンジニアのノート

GraphQL for .NET で Azure Functions + Cosmos DB を試してみた

Azure Functions で .NET を使って GraphQL の QueryとMutation を試してみました。
以下のブログと紹介されているコードを読めば特に説明不要ですね。

www.tpeczek.com

www.tpeczek.com

追加するするパッケージも以下の3つだけ。

  • GraphQL.Server.Core
  • Microsoft.Azure.Functions.Extensions
  • Microsoft.Azure.WebJobs.Extensions.CosmosDB

デモのInfrastructure内のコードやStartupなどは、ほぼそのまま。モデルやスキーマをちょっと変えて試してみました。

Model と Type

public class Person
{
    public string Id { get; set; }
    public string Firstname { get; set; }
    public int Age { get; set; }
}
public sealed class PersonType : ObjectGraphType<Person>
{
    public PersonType()
    {
        Name = "Person";

        Field(x => x.Id, type: typeof(IdGraphType)).Description("ID");
        Field(x => x.Firstname).Description("Firstname");
        Field(x => x.Age).Description("Age");
    }
}

Query

public class SampleQuery : ObjectGraphType
{
    private static readonly Uri CollectionUri = UriFactory.CreateDocumentCollectionUri("SampleDB", "People");

    public SampleQuery(IDocumentClient documentClient)
    {
        // 全件取得
        Field<ListGraphType<PersonType>>(
            "people",
            resolve: context => documentClient.CreateDocumentQuery<Person>(CollectionUri, null)
        );

        // IDを指定して取得
        FieldAsync<PersonType>(
            "person",
            arguments: new QueryArguments(
                new QueryArgument<IdGraphType> { Name = "id", Description = "ID" }),
            resolve: async context =>
            {
                var id = context.GetArgument<string>("id");
                var uri = UriFactory.CreateDocumentUri("SampleDB", "People", id);
                var document = await documentClient.ReadDocumentAsync<Person>(uri, new RequestOptions { PartitionKey = new PartitionKey(id) });

                return document.Document;
            });
    }
}

Mutation

public class SampleMutation : ObjectGraphType
{
    private static readonly Uri CollectionUri = UriFactory.CreateDocumentCollectionUri("SampleDB", "People");

    public SampleMutation(IDocumentClient documentClient)
    {
        // 追加
        FieldAsync<PersonType>(
            "createPerson",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<StringGraphType>>
                {
                    Name = "firstname"
                },
                new QueryArgument<NonNullGraphType<IntGraphType>>
                {
                    Name = "age"
                }),
            resolve: async context =>
            {
                var firstname = context.GetArgument<string>("firstname");
                var age = context.GetArgument<int>("age");
                var id = Guid.NewGuid().ToString();
                var person = new Person
                {
                    Id = id,
                    Firstname = firstname,
                    Age = age
                };

                await documentClient.CreateDocumentAsync(CollectionUri, person);

                return person;
            }
        );

        // 更新
        FieldAsync<PersonType>(
            "updatePerson",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id" },
                new QueryArgument<StringGraphType>
                {
                    Name = "firstname"
                },
                new QueryArgument<IntGraphType>
                {
                    Name = "age"
                }
                ),
            resolve: async context =>
            {
                var id = context.GetArgument<string>("id");
                var firstname = context.GetArgument<string>("firstname");
                var age = context.GetArgument<int>("age");

                var uri = UriFactory.CreateDocumentUri("SampleDB", "People", id);
                var person = new Person
                {
                    Id = id,
                    Firstname = firstname,
                    Age = age
                };
                await documentClient.ReplaceDocumentAsync(uri, person);
                return person;

            });

        // 削除
        FieldAsync<StringGraphType>(
            "deletePerson",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<StringGraphType>>
                {
                    Name = "id"
                }
            ),
            resolve: async context =>
            {
                var id = context.GetArgument<string>("id");

                var uri = UriFactory.CreateDocumentUri("SampleDB", "People", id);
                await documentClient.DeleteDocumentAsync(uri, new RequestOptions { PartitionKey = new PartitionKey(id) });
                return id;
            });

    }
}

Schema

public class SampleSchema : GraphQL.Types.Schema
{
    public SampleSchema(IDependencyResolver dependencyResolver) : base(dependencyResolver)
    {
        Query = dependencyResolver.Resolve<SampleQuery>();
        Mutation = dependencyResolver.Resolve<SampleMutation>();
    }
}

クエリ

GraphiQL はないで、Postman で GraphiQL を指定して動作確認して無事完了。 f:id:taka1923:20200804111244p:plain

クエリはこんな感じです。

取得

query { 
    people { 
        id
        firstname
        age 
    }
}
query
{
  person (id : "00000000-0000-0000-bed0-3d36769a33c0")
  {
    id
    firstname
    age
  }
}

追加

mutation
{
  createPerson(
    firstname: "太郎" 
    age: 10
  )
  {
    id
    firstname
    age
  }
}

更新

mutation
{
  updatePerson(
    id : "00000000-0000-0000-bed0-3d36769a33c0"
    firstname: "次郎" 
    age: 20
  )
  {
    id
    firstname
    age
  }
}

削除

mutation
{
  deletePerson(
    id : "00000000-0000-0000-bed0-3d36769a33c0"
  )
}

Subscription

Subscription には対応していないので、通知は SignalR を使うのが簡単ですね。Cosmos DB の変更通知でしたら、以下で紹介されているシナリオと同じです。

docs.microsoft.com

ASP.NET Core ではないですが、Azure Functions と Azure SignalR Service は Microsoft Learn で試すことも可能です。

docs.microsoft.com