spring-graphql-demo

動作確認

まずは起動する。

./mvnw spring-boot:run

curl(とjq)で動作確認してみる。

$ curl -s localhost:8080/graphql -XPOST -H "Content-Type: application/json" -d '{"query":"{hello}"}' | jq
{
  "data": {
    "hello": "Hello, world!"
  }
}

spring.graphql.schema.printer.enabled=trueを設定していると http://localhost:8080/graphql/schema でスキーマを確認できる。

spring.graphql.graphiql.enabled=trueを設定していると http://localhost:8080/graphiql でクエリエディター(GraphiQL)を使える。

コードに関するメモ

スキーマ定義ファイルはsrc/main/resources/graphql/schema.graphqls。 data fetchingの定義はcom.example.helloworld.HelloWorldDataWiringで行っている。

Web MVCで使う場合はorg.springframework.graphql.boot.GraphQlWebMvcAutoConfigurationで自動設定されるみたい。

プロパティはorg.springframework.graphql.boot.GraphQlPropertiesを見れば把握できそう。

graphql.schema.DataFetcher

graphql.schema.DataFetcherjava.util.Optionalを返しても良いっぽい(BookDataWiringBookRepositoryあたりを参照)。

graphql.schema.DataFetchingEnvironmentからgetSourceで現在処理しているtypeを取得したりgetArgumentでクエリーパラメーターを取得して子typeを取得することができる。

org.dataloader.DataLoader

typeを辿るとき愚直に実装をするとN + 1問題が発生する。 それを解消するためorg.dataloader.DataLoaderというクラスがある。

まずorg.dataloader.DataLoaderを準備する。 今回はorg.dataloader.BatchLoaderインターフェースを実装したクラスを作ってDataLoader.newDataLoaderファクトリーメソッドでDataLoaderを準備している。 実装例はcom.example.tweet.UserLoaderを参照すること。

それから実装したDataLoaderorg.dataloader.DataLoaderRegistryに登録して、そのorg.dataloader.DataLoaderRegistrygraphql.ExecutionInputへセットする。 graphql.ExecutionInputへセットする方法はWeb MVCであればorg.springframework.graphql.web.WebInterceptorを用いる。 ドキュメントはWeb Interceptionのセクションを参照すること。 実装例はcom.example.tweet.TweetWebInterceptorを参照すること。

最後にdata fetchingの定義でDataFetchingEnvironment.getDataLoaderを使ってDataLoaderを取得して、loadメソッドを呼び出す。 実装例はcom.example.tweet.TweetDataWiringを参照すること。

ちなみにloadメソッドの戻り値はjava.util.concurrent.CompletableFutureとなっている。 つまりgraphql.schema.DataFetcherはシンプルな値の他にjava.util.Optionaljava.util.concurrent.CompletableFutureが返せるということになる。

ページング

GraphQLの公式ページでページングの方式について触れつつ、オススメの方式としてRelayのCursor Connectionsの仕様を紹介している。

RelayというのはFacebookが提供しているGraphQLクライアントライブラリ。

実装例はcom.example.todo.TaskDataWiringを参考にすること。

DataFetcher.getで返せる型

  • Optional
    • DefaultValueUnboxerunwrap
  • StreamIterator、配列
    • FpKitIterableへ変換
  • MonoFlux
    • ContextDataFetcherDecoratorunwrap
  • DataFetcherResult
    • ExecutionStrategy.unboxPossibleDataFetcherResultunwrapされる
  • CompletionStage
    • Async.toCompletableFutureCompletableFutureへ変換される

参考リソース