まずは起動する。
./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
はjava.util.Optional
を返しても良いっぽい(BookDataWiring
とBookRepository
あたりを参照)。
graphql.schema.DataFetchingEnvironment
からgetSource
で現在処理しているtype
を取得したりgetArgument
でクエリーパラメーターを取得して子type
を取得することができる。
子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
を参考にすること。
Optional
DefaultValueUnboxer
でunwrap
Stream
、Iterator
、配列FpKit
でIterable
へ変換
Mono
、Flux
ContextDataFetcherDecorator
でunwrap
DataFetcherResult
ExecutionStrategy.unboxPossibleDataFetcherResult
でunwrap
される
CompletionStage
Async.toCompletableFuture
でCompletableFuture
へ変換される