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.DataFetcherはjava.util.Optionalを返しても良いっぽい(BookDataWiringとBookRepositoryあたりを参照)。
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を参照すること。
それから実装したDataLoaderをorg.dataloader.DataLoaderRegistryに登録して、そのorg.dataloader.DataLoaderRegistryをgraphql.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.Optionalやjava.util.concurrent.CompletableFutureが返せるということになる。
ページング
GraphQLの公式ページでページングの方式について触れつつ、オススメの方式としてRelayのCursor Connectionsの仕様を紹介している。
RelayというのはFacebookが提供しているGraphQLクライアントライブラリ。
実装例はcom.example.todo.TaskDataWiringを参考にすること。
DataFetcher.getで返せる型
OptionalDefaultValueUnboxerでunwrap
Stream、Iterator、配列FpKitでIterableへ変換
Mono、FluxContextDataFetcherDecoratorでunwrap
DataFetcherResultExecutionStrategy.unboxPossibleDataFetcherResultでunwrapされる
CompletionStageAsync.toCompletableFutureでCompletableFutureへ変換される
