graphql-java-toolsを使ってGraphQLサーバーを作ってみる!
GraphQLサーバを作る際には graphql-java-tools を使うと便利らしいので使ってみました。
graphql-java-tools について
graphql-java だけでは冗長になりがちな仕組みを提供してくれるライブラリみたいです。すでにプロジェクトで独自のドメイン似特化したPOJOがある場合に、GraphQLとシームレスに統合できるようになっているんだとか。Javaとライブラリ名にありますが、JVM上なら動作するためKotlinでも使うことができるみたいですね。
GraphQLは関連するライブラリが多いように感じます。Qiitaのこの記事にそれぞれのライブラリの関連性が書かれています。この記事によれば graphql-java-tools はGraphQL関連に関して graphql-java のみに依存しているみたいです。
queryの実装
動作環境は以下の通りです。
> java -version java version "1.8.0_191" Java(TM) SE Runtime Environment (build 1.8.0_191-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode) > gradle -version ------------------------------------------------------------ Gradle 6.0.1 ------------------------------------------------------------ Build time: 2019-11-18 20:25:01 UTC Revision: fad121066a68c4701acd362daf4287a7c309a0f5 Kotlin: 1.3.50 Groovy: 2.5.8 Ant: Apache Ant(TM) version 1.10.7 compiled on September 1 2019 JVM: 1.8.0_191 (Oracle Corporation 25.191-b12) OS: Mac OS X 10.15.3 x86_64
graphql-java-tools を試すに当たり、 graphql-spring-boot を使用することにしました。このライブラリは graphql-java-tools も含んでいるのでこちらのライブラリだけを依存先に追加するだけでよいみたいです。 build.gradle.kts の全文は最後の補足に乗せてあります。
GraphQLスキーマは以下になります。
type Query {
bookMarks: [BookMark!]!
}
type BookMark {
title: String!
url: String!
}
上記のスキーマに合わせてQueryの実装をしていくのですが、graphql-java-tools では Resolver というクラスをGraphQLスキーマに対応付けします。 簡単に Resolver の立ち位置をスケッチしてみました。

Queryでは GraphQLQueryResolver インターフェイスを継承したResolverクラスをGraphQLスキーマに対応付けます。bookMarks() メソッドはGraphQLスキーマの bookMarks: [BookMark!]! に対応しています。この仕組があることで、GraphQLスキーマとそれを処理する実装の紐づきが明確になっているように感じます。
package demo.tools.graphql import com.coxautodev.graphql.tools.GraphQLQueryResolver import org.springframework.stereotype.Component @Component class Query(val bookMarkRepository: BookMarkRepository) : GraphQLQueryResolver { fun bookMarks(): List<BookMark> { return bookMarkRepository.getAll() } }
この Query クラスを使ってGraphQLインスタンスを生成すれば実装は終わりです。 以下のコードではSchemaParser.newParser() によってGraphQLインスタンスを生成しています。これも graphql-java-tools による機能です。 上記の Query クラスは resolvers() メソッドで渡します。
package demo.tools.graphql import com.coxautodev.graphql.tools.GraphQLMutationResolver import com.coxautodev.graphql.tools.GraphQLQueryResolver import com.coxautodev.graphql.tools.SchemaParser import graphql.GraphQL import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean @SpringBootApplication class DemoApplication { @Bean fun graphQL(query: GraphQLQueryResolver): GraphQL { val graphQLSchema = SchemaParser.newParser() .file("schema.graphqls") .resolvers(query) .build() .makeExecutableSchema() return GraphQL.newGraphQL(graphQLSchema).build() } } fun main(args: Array<String>) { runApplication<DemoApplication>(*args) }
mutationの実装
mutationも上記のやり方とほとんど同じです。 Query の代わりに Mutation クラスを作り、登録します。
まずはGraphQLSchemaにMutationを追加します。
type Query {
bookMarks: [BookMark!]!
}
type Mutation {
registerBookMark(title: String, url: String): BookMark!
}
type BookMark {
title: String!
url: String!
}
次に対応する Mutation クラスを実装します。
package demo.tools.graphql import com.coxautodev.graphql.tools.GraphQLMutationResolver import org.springframework.stereotype.Component @Component class Mutation(val bookMarkRepository: BookMarkRepository) : GraphQLMutationResolver { fun registerBookMark(title: String, url: String): BookMark { val aNewBookMark = BookMark(title, url) bookMarkRepository.save(aNewBookMark) return aNewBookMark } }
最後に Query を作成したときと同様、 GraphQL インスタンスに Mutation インスタンスを登録します。
エンドポイントを叩いてみる
実際にサーバーに対してリクエストを投げてみました。リクエスト・レスポンスの送受信には GraphQL IDE を使いました。
では、Mutationによって BookMark を登録してみます。
// リクエスト
mutation registerBookMark($title: String, $url: String) {
registerBookMark(title: $title, url: $url) {
title
url
}
}
{
"title": "demoo",
"url": "http://demo.com"
}
// レスポンス
{
"data": {
"registerBookMark": {
"title": "demoo",
"url": "http://demo.com"
}
}
}
Query で全件取得をしてみます。
// リクエスト
{
bookMarks {
title
url
}
}
// レスポンス
{
"data": {
"bookMarks": [
{
"title": "google",
"url": "https://google.com"
},
{
"title": "demoo",
"url": "http://demo.com"
}
]
}
}
補足
今回使用したプロジェクトの build.gradle.kts は以下になります。
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.2.4.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE"
kotlin("jvm") version "1.3.61"
kotlin("plugin.spring") version "1.3.61"
}
group = "com.graphql-java.tutorial"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
dependencies {
implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:6.0.0")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}