原文はこちら。
The original article was written by Tim Middleton (Solution Architect with Oracle Coherence Development).
https://medium.com/helidon/microprofile-graphql-support-now-available-in-helidon-mp-dbc7bc0b4af
We are pleased to announce that Helidon MP version 2.2.0 がいよいよEclipse FoundationのMicroProfile GraphQL仕様をサポートすることを発表できうれしく思っています。
Microprofile GraphQL Specification
https://github.com/eclipse/microprofile-graphql
仕様には以下のような文言があります。
GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL interprets strings from the client, and returns data in an understandable, predictable, pre-defined manner. GraphQL is an alternative, though not necessarily a replacement for REST.(GraphQLはAPIのためのオープンソースのデータクエリおよびデータ操作言語で、既存のデータを使ってクエリを実行するためのランタイムです。GraphQLはクライアントからの文字列を解釈し、わかりやすく、予測可能で、事前に定義された方法でデータを返します。GraphQLは、RESTの代替品ではありますが、必ずしもRESTを置き換えるものではありません。)
https://github.com/eclipse/microprofile-graphql#about
GraphQLは、クエリ(query)、ミューテーション(mutation)、サブスクリプション(subscription)の3種類のデータ操作を提供します。スキーマは GraphQL の中核となるもので、API がサポートする操作を明確に定義します。
スキーマの例を以下に示します。これは架空のタスクAPIを定義しており、後ほど説明いたします。
type Mutation {
"Create a task with the given description"
createTask(description: String): Task
"Remove all completed tasks and return the tasks left"
deleteCompletedTasks: [Task]
"Delete a task and return the deleted task details"
deleteTask(id: String): Task
"Update a task"
updateTask(completed: Boolean,
description: String,
id: String): Task
}
type Query {
"Return a given task"
findTask(id: String): Task
"Query tasks and optionally specified only completed"
tasks(completed: Boolean): [Task]
}
type Task {
completed: Boolean!
createdAt: BigInteger!
description: String!
id: String!
}
"Built-in java.math.BigInteger"
scalar BigInteger
スキーマを見ると、多くの変異とクエリが定義されていることがわかります。例えば、findTask
クエリは、タスクid
としてString
の入力型を定義し、Taskオブジェクトを返します。tasks
クエリは、オプションのcompleted
という引数を定義し、Task
オブジェクトの配列を返します。
ではこのサンプルAPIをより詳細に説明していきます。
注意: GraphQLの初心者であれば、先に進む前に以下を読んでおくと良いでしょう。
GraphQL – A query language for your APIhttps://graphql.org/
The MicroProfile GraphQL Specification
MicroProfile GraphQL仕様 (1.0.3) からの引用です。Taken from https://github.com/eclipse/microprofile-graphql (1.0.3) からの引用です。
The intent of the MicroProfile GraphQL specification is to provide a “code-first” set of APIs that will enable users to quickly develop portable GraphQL-based applications in Java.(MicroProfile GraphQL仕様の意図は、ユーザがJavaでポータブルなGraphQLベースのアプリケーションを迅速に開発できるようにするための、 code-firstな一連のAPIを提供することである)
There are 2 main requirements for all implementations of this specification, namely:(この仕様のすべての実装には、主に2つの要件がある)
1. Generate and make the GraphQL Schema available. This is done by looking at the annotations in the users code and must include all GraphQL Queries and Mutations as well as all entities as defined implicitly via the response type or argument(s) of Queries and Mutations.(GraphQLスキーマを生成して利用可能にする。これは、ユーザーコードのアノテーションを見て行われる。すべてのGraphQLクエリとミューテーション、およびクエリとミューテーションのレスポンスの型または引数を使って暗黙的に定義されたすべてのエンティティを含まなければならない)2. Execute GraphQL requests. This will be in the form of either a Query or a Mutation. As a minimum the specification must support executing these requests via HTTP.(GraphQLリクエストを実行する。これは、クエリまたはミューテーションのいずれかの形式である。最低限、仕様はHTTP経由でのリクエストの実行をサポートしなければならない。)
Note: The spec does not yet contain support for subscriptions.(注意:この仕様ではまだサブスクリプションをサポートしていない)
MicroProfile GraphQL
https://download.eclipse.org/microprofile/microprofile-graphql-1.0.3/microprofile-graphql.html#microprofile_graphql
ハイレベルには、この仕様ではcode firstアプローチでAPIを開発するために利用可能な多くのアノテーションを定義しています。
- GraphQLApi — CDI BeanをGraphQLエンドポイントとして識別するマーカーアノテーション
- Query — オブジェクトまたはオブジェクトのコレクションのすべてのフィールドまたは特定のフィールドを問い合わせることができるパブリック・メソッドに適用される。これらは
GraphQLApi
アノテーションが付いたクラスに存在する必要がある。 - Mutation — ユーザーがエントリを作成したり、変異させたりすることができるパブリック・メソッドに適用される。これも、GraphQLApiアノテーションが付いたクラス内に存在する必要がある。
その他の関連するアノテーションには以下のようなものがあります。
- Type — 複合型(POJO)を出力型として定義
- Input — 複合他がを入力型として定義
- Interface — 型をインターフェースとして定義
Helidon Support for the MP GraphQL Spec
Helidonバージョン2.2.0は、MicroProfile GraphQL仕様のバージョン1.0.3をサポートし、合格しています。
Include Dependencies
まず、HelidonドキュメントのManaging Dependenciesのセクションで説明されているように、Helidon BOMを含める必要があります。
Managing Dependencies
https://helidon.io/docs/v2/#/about/04_managing-dependencies
次に、実装を使用するためには、以下のGraphQLの依存関係もプロジェクトに含める必要があります。
<dependency>
<groupId>io.helidon.microprofile.graphql</groupId>
<artifactId>helidon-microprofile-graphql-server</artifactId>
</dependency>
注意: Helidon SEでもGraphQLをサポートしますが、GraphQLスキーマとバインディングを手作業で作成しなければなりません。詳細はHelidon SEのドキュメントをご覧ください。
SE — Helidon SE
https://helidon.io/docs/v2/#/se/introduction/01_introduction
上記の依存関係を含めたら、GraphQLエンドポイントのコーディングを開始できます。
Coding the Example
注意: 以下の例はHelidon MP GraphQLのサンプルからの引用です。サンプルは以下のURLから利用できます。
Microprofile GraphQL Example
https://github.com/oracle/helidon/tree/master/examples/microprofile/graphql
Create Your API Class
クラスを作成し、@GraphQLAPI
と @ApplicationScoped
を使って注釈を付けます。この例では、Task
を ConcurrentHashMap
に格納しますが、Oracle Coherence をバックエンドとして使用する方法については、次回の記事でご紹介する予定です。
Coherence Community
https://coherence.community/
@GraphQLApi
@ApplicationScoped
public class TaskApi {
// store the Tasks in a Map
private Map<String, Task> tasks = new ConcurrentHashMap<>();
...}
Create an endpoint to create a new Task
以下のメソッドではdescription
を受け入れ、新たなTask
を作成し、作成済みのTask
を返すミューテーションを定義しています
@Mutation
@Description("Create a task with the given description")
public Task createTask(@Name("description") String description) {
if (description == null) {
throw new IllegalArgumentException(
"Description must be provided");
}
Task task = new Task(description);
tasks.put(task.getId(), task);
return task;
}
上記メソッドでいくつか注意すべきポイントがあります。
@Description
により、エンドポイントをドキュメント化し、スキーマで利用可能にします。@Name
でパラメータに名前を付けることを保証します。- 仕様では、チェック済み例外がスローされた場合、例外メッセージが表示されます。未チェックの例外の場合(例:
IllegalArgumentException
)はメッセージ出力が抑止されていますが、この挙動を変更できます。サンプルのMETA-INF/microprofile-config.properties
を参照ください。
これを元に自動生成されたGraphQL Schemaは以下の通りです。
type Mutation {
"Create a task with the given description"
createTask(description: String): Task
}
Create an endpoint to display Tasks
以下のメソッドでは、完了済みタスクを表示するか否かを判断するため、オプションのBoolean値を指定して全てのTaskを表示するクエリを定義しています。
@Query
@Description("Query tasks and optionally specified only completed")
public Collection<Task> getTasks(@Name("completed")
Boolean completed) {
return tasks.values().stream()
.filter(task -> completed == null
|| task.isCompleted() == completed)
.collect(Collectors.toList());
}
Create an endpoint to return an individual Task
以下のメソッドでは、id
で指定された個別のTask
を返すクエリを定義しています。
@Query
@Description("Return a given task")
public Task findTask(@Name("id") String id)
throws TaskNotFoundException {
return Optional.ofNullable(tasks.get(id))
.orElseThrow(() ->
new TaskNotFoundException("Task not found " + id));
}
上記メソッドがTaskNotFoundException
をスローする場合、例外メッセージの表示がデフォルトの挙動です。
上記の2個のクエリから自動生成されたGraphQLスキーマは以下のようです。
type Query {
"Return a given task"
findTask(id: String!): Task
"Query tasks and optionally specify only completed"
tasks(completed: Boolean): [Task]
}
サンプルの全てのメソッドを説明しませんが、このサンプルを見れば一般的なアイデアが得られるはずです。
Running the Example
Taskサンプルを実行するには以下の手順で実行します。
注意 : JDK 11以上である必要があります。
- HelidonのリポジトリをクローンしてHelidonをビルド
git clone https://github.com/oracle/helidon.git
mvn clean install -DskipTests
2. サンプルをビルドし実行
cd examples/microprofile/graphql
mvn clean install
java -jar target/helidon-examples-microprofile-graphql.jar
起動後、ログにいくつか出力されていることに気づくはず(便宜上短縮している)。
... Discovered 1 annotated GraphQL API class... Server started on http://localhost:7001... You are using experimental features. These APIs may change, please follow changelog!
... Experimental feature: GraphQL (GraphQL)
上記の例では、Helidon MP GraphQLがGraphQLApiアノテーションが付いた1個のクラスを発見し、サーバーがポート番号7001で開始し、現在のGraphQLのバージョンがexperimental(実験的)であることを示している。
3. 続いて、スキーマを取得する。GraphQL仕様によると、スキーマは http://host:port/graphql/schema.graphql
で利用可能でなければならない。
$ curl http://127.0.0.1:7001/graphql/schema.graphqltype Mutation {
"Create a task with the given description"
createTask(description: String): Task
"Remove all completed tasks and return the tasks left"
deleteCompletedTasks: [Task]
"Delete a task and return the deleted task details"
deleteTask(id: String): Task
"Update a task"
updateTask(completed: Boolean, description:
String, id: String): Task
}type Query {
"Return a given task"
findTask(id: String): Task
"Query tasks and optionally specify only completed"
tasks(completed: Boolean): [Task]
}type Task {
completed: Boolean!
createdAt: BigInteger!
description: String!
id: String!
}"Custom: Built-in java.math.BigInteger"
scalar BigInteger
4. curlでcreateTask
ミューテーションを呼び出すと、新規作成されたTask
オブジェクトが返る
curl -X POST http://127.0.0.1:7001/graphql -d '{"query":"mutation createTask { createTask(description: \"Task Description 1\") { id description createdAt completed }}"}'{
"data":{"createTask":
{
"id":"0d4a8d",
"description":"Task Description 1",
"createdAt":1605501774877,
"completed":false
}
}
Using the GraphiQL UI
GraphiQL UIは、GraphQLコマンドを実行するためのUIを提供します。
GraphQL IDE Monorepo
https://github.com/graphql/graphiql
このツールはHelidonのMicroProfile実装にはデフォルトで含まれてはいません。以下のガイドに従い、UIをGraphQLアプリケーションに組み込むことができます。
注意: 以下はGraphQL MicroProfileのサンプルからの引用です。サンプルは以下のURLから利用できます。
Microprofile GraphQL Example
https://github.com/oracle/helidon/tree/master/examples/microprofile/graphql
- ここからサンプルのindex.htmlファイルの内容を
examples/microprofile/graphql/src/main/resources/web/index.html
にコピー fetch('https://my/graphql', {
の行のURL(この例ではhttps://my/graphql)をhttp://127.0.0.1:7001/graphql
に変更- 上記指示に従い、サンプルを再ビルドして実行
http://127.0.0.1:7001/ui
でGraphiQL UIにアクセス

最後に、以下のコマンドを左側のエディターにコピーし、Playボタンを押して個々のミューテーションやクエリを実行してください。
# Fragment to allow shorcut to display all fields for a task
fragment task on Task {
id
description
createdAt
completed
}
# Create a task
mutation createTask {
createTask(description: "Task Description 1") {
...task
}
}
mutation createTaskWithoutDescription {
createTask {
...task
}
}
# Find all the tasks
query findAllTasks {
tasks {
...task
}
}
# Find a task
query findTask {
findTask(id: "251474") {
...task
}
}
# Find completed Tasks
query findCompletedTasks {
tasks(completed: true) {
...task
}
}
# Find outstanding Tasks
query findOutstandingTasks {
tasks(completed: false) {
...task
}
}
mutation updateTask {
updateTask(id: "251474" description:"New Description") {
...task
}
}
mutation completeTask {
updateTask(id: "251474" completed:true) {
...task
}
}
# Delete a task
mutation deleteTask {
deleteTask(id: "1f6ae5") {
...task
}
}
# Delete completed
mutation deleteCompleted {
deleteCompletedTasks {
...task
}
}
META-INF/microprofile-config.properties
には以下のGraphiQLを実行支援するものが含まれています。
server.static.classpath.context=/ui
server.static.classpath.location=/web
graphql.cors=Access-Control-Allow-Origin
mp.graphql.exceptionsWhiteList=java.lang.IllegalArgumentException
最初の2個のプロパティは /ui
を resources/web
にマッピングすることで、index.html
が表示されます。
3番目のプロパティでCORSを許可するため、GraphiQLインターフェースが動作します。
通常未チェックの例外は表示されませんが、4個目のプロパティでオーバーライドしています。
Where to From Here?
GraphQLとHelidonについて詳細を知りたい方は以下のリソースをどうぞ。
- About GraphQL
https://graphql.org/ - Helidon documentation
https://helidon.io/docs/v2 - Helidon on GitHub
https://github.com/oracle/helidon - Helidon MP GraphQL Example
https://github.com/oracle/helidon/tree/master/examples/microprofile/graphql