graphql-java-tools
のResolverを使ってネストされたPOJOをクライアントに返却してみました。
Resolverについて
graphql-java-tools
においてResolverとはGraphQLスキーマに対応したPOJOをクライアントに返却する際にデータを解決するインスタンスです。ライブラリ内ではResolverはインターフェイスとして定義されており、使う側はそれらのインターフェイスを実装することでResolverのインスタンスを作ります。ライブラリ内に定義されているインターフェイスの関係性をざっくり書いてみました。
前回の記事では Query Resolver と Mutation Resolver を実装する例を載せました。一方、今回はネストされたPOJOのResolverの実装をしてみました。
ネストされたPOJOとは?
"ネストされたPOJO"という表現が適切なのかわかりませんが、あるオブジェクトAが別のオブジェクトBをID参照で関連を持っているときに、GraphQLでAオブジェクトとBオブジェクトを一度のQueryで取得するような場合の事を指します。例えば以下のようなGraphQLスキーマがあったときに...
type Query { bookMarks: [BookMark!]! categories: [Category!]! } type Mutation { registerBookMark(title: String, url: String): BookMark! } type Category { name: String! bookMarks: [BookMark!]! } type BookMark { title: String! url: String! }
Category
と BookMark
のクラス定義が以下のように定義され、IDによって参照を保持している場合です。
// CategoryはbookMarksフィールドによってBookMarkへの参照を持つ data class Category(val id: Int, val name: String, val bookMarks: List<Int>) data class BookMark(val id: Int, val title: String, val url: String)
このとき、クライアントは以下のようなクエリが発行できます。
query categories { categories { name bookMarks { title url } } }
ここでは単純に Category
だけを取得して返却することはできません。なぜならクライアントは Category
が持っている BookMark
のID値ではなく、 BookMark
自体の情報を求めているからです。graphql-java-tools
ではこのような場合に Category
と BookMark
の両方を合わせて返却する仕組みを提供しています。
Resolverの実装
このような場合、QueryのもととなるPOJOに対応づく GraphQLResolver
を実装した独自のResolverインスタンスを作る必要があります。Category
に対するResolverは以下のようになります。このResolverの bookMarks()
メソッドは、GraphQLの bookMark
フィールドに対応しています。
package demo.tools.graphql import com.coxautodev.graphql.tools.GraphQLResolver import org.springframework.stereotype.Component import java.lang.IllegalStateException @Component class CategoryResolver(val bookMarkRepository: BookMarkRepository) : GraphQLResolver<Category> { // GraphQL の bookMark に対応 fun bookMarks(category: Category): List<BookMark> { return category.bookMarks .map { bookMarkRepository.findById(it) ?: throw IllegalStateException() } } }
Queryに対応しているResolverは前回の記事と同様の形で実装します。
package demo.tools.graphql import com.coxautodev.graphql.tools.GraphQLQueryResolver import org.springframework.stereotype.Component @Component class Query( val bookMarkRepository: BookMarkRepository, val categoryRepository: CategoryRepository ) : GraphQLQueryResolver { fun categories(): List<Category> { return categoryRepository.getAll() } fun bookMarks(): List<BookMark> { return bookMarkRepository.getAll() } }
このような実装を行うことでネストされたPOJOを返却する事ができます。
エンドポイントを叩いてみる
実際にエンドポイントを叩いてみました。
// リクエスト query categories { categories { name bookMarks { title url } } }
// レスポンス { "data": { "categories": [ { "name": "Most Visit", "bookMarks": [ { "title": "google", "url": "https://google.com" } ] } ] } }