ASP.NET Core MVC を使って GraphQL API のエンドポイントを実装してきたけど、ASP.NET Core 用の GraphQL Server 実装が存在することを今更知った。
気分は某CMの松重豊。「それさぁ、早くいってよぉ~」って感じだ。 いやまぁ、ちゃんと調べなかった自分が悪いんだけど。
Web ブラウザ上で GraphQL を試せる GraphQL IDE も、 GraphiQL だけでなく GraphQL Playground とかに対応しているみたいだ。
先日のサンプルを GraphQL.Server.Transports.AspNetCore
を使ったものに書き換えてみた。GraphQL IDE は GraphiQL ではなく GraphQL Playground を選択。
using GraphQL; using GraphQL.Server; using GraphQL.Server.Ui.Playground; using GraphQL.Types; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System.Linq; namespace HelloGraphQL { //============================================ // 1. Entity Framework Core で使うクラスを定義 //============================================ public class Player { public int Id { get; set; } public string Name { get; set; } public string Position { get; set; } } public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Player> Players => Set<Player>(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Player>(e => { e.Property(x => x.Id).IsRequired(); e.Property(x => x.Name).IsRequired(); e.Property(x => x.Position).IsRequired(); e.HasKey(x => x.Id); }); } // テスト用のデータを登録する public void EnsureSeedData() { if (!Players.Any()) { Players.AddRange( new Player { Name = "メッシ", Position = "FW", }, new Player { Name = "スアレス", Position = "FW", }, new Player { Name = "ベイル", Position = "FW", }, new Player { Name = "モドリッチ", Position = "MF", } ); SaveChanges(); } } } //============================== // 2. GraphQL で使うクラスを定義 //============================== // Player に対応する GraphQL の型 public class PlayerType : ObjectGraphType<Player> { public PlayerType() { Field(x => x.Id); Field(x => x.Name); Field(x => x.Position); } } // クエリを定義 public class HelloGraphQLQuery : ObjectGraphType { public HelloGraphQLQuery() { Field<ListGraphType<PlayerType>>( "players", arguments: new QueryArguments( new QueryArgument<IntGraphType> { Name = "id", }), resolve: context => { var dbContext = (ApplicationDbContext)context.UserContext; var query = dbContext.Players.AsQueryable(); if (context.HasArgument("id")) { var id = context.GetArgument<int>("id"); query = query.Where(b => b.Id == id); } return query.ToList(); }); } } // ミューテーションを定義 public class HelloGraphQLMutation : ObjectGraphType { public HelloGraphQLMutation() { Field<PlayerType>( "createPlayer", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<StringGraphType>>() { Name = "name", }, new QueryArgument<NonNullGraphType<StringGraphType>>() { Name = "position", } ), resolve: context => { var dbContext = (ApplicationDbContext)context.UserContext; var name = context.GetArgument<string>("name"); var position = context.GetArgument<string>("position"); var player = new Player() { Name = name, Position = position, }; dbContext.Players.Add(player); dbContext.SaveChanges(); return player; }); Field<PlayerType>( "updatePlayer", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<IntGraphType>>() { Name = "id", }, new QueryArgument<StringGraphType>() { Name = "name", }, new QueryArgument<StringGraphType>() { Name = "position", } ), resolve: context => { var dbContext = (ApplicationDbContext)context.UserContext; var id = context.GetArgument<int>("id"); var player = dbContext.Players.Find(id); if (player != null) { if (context.HasArgument("name")) { player.Name = context.GetArgument<string>("name"); } if (context.HasArgument("position")) { player.Position = context.GetArgument<string>("position"); } dbContext.Players.Update(player); dbContext.SaveChanges(); return player; } else { return null; } }); Field<PlayerType>( "deletePlayer", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<IntGraphType>>() { Name = "id", } ), resolve: context => { var dbContext = (ApplicationDbContext)context.UserContext; var id = context.GetArgument<int>("id"); var player = dbContext.Players.Find(id); if (player != null) { dbContext.Players.Remove(player); dbContext.SaveChanges(); return player; } else { return null; } }); } } // GraphQL のスキーマを定義 public class HelloGraphQLSchema : Schema { public HelloGraphQLSchema(IDependencyResolver dependencyResolver) : base(dependencyResolver) { Query = dependencyResolver.Resolve<HelloGraphQLQuery>(); Mutation = dependencyResolver.Resolve<HelloGraphQLMutation>(); } } //============================================================== // 3. ASP.NET Core 用の Startup と Program を作成 //============================================================== public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // GraphQL Server 用 services.AddGraphQL() .AddUserContextBuilder(httpContext => { return httpContext.RequestServices .GetService<ApplicationDbContext>(); }); // GraphQL 用 services.AddSingleton<PlayerType>(); services.AddSingleton<HelloGraphQLQuery>(); services.AddSingleton<HelloGraphQLMutation>(); services.AddSingleton<ISchema, HelloGraphQLSchema>(); services.AddSingleton<IDocumentExecuter, DocumentExecuter>(); services.AddSingleton<IDependencyResolver>(x => new FuncDependencyResolver(x.GetService)); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext dbContext) { // GraphQL Server を使う app.UseGraphQL<ISchema>("/graphql"); // GraphQL Playground を使う app.UseGraphQLPlayground(new GraphQLPlaygroundOptions { Path = "/ui/playground", }); // テスト用データベースが無ければ作る dbContext.Database.EnsureCreated(); dbContext.EnsureSeedData(); } } public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); } }
デバッグ実行して、/ui/playground
を表示。
クエリを実行してみたところ、ちゃんと GraphQL API として動作した。
AddUserContextBuilder
のところで単純に ApplicationDbContext
を返したけど、独自のコンテキストを定義して、その中にいろいろ必要な値を詰めてやれば、この GraphQL Server でも結構やれそう。まず気になるのは認証・認可なので、次はそこら辺を調べたい。