Developing a GraphQL client on MicroProfile with Quarkus

原文はこちら。
The original entry was written by Jean-François James (Senior Software Architect, Head of Worldline Expert Community).
https://jefrajames.fr/2020/04/20/developing-a-graphql-client-on-microprofile-with-quarkus/

MicroProfile GraphQL 1.0のリリースをうけて、Phillip KrügerがGraphQLとサーバーの開発方法について説明した記事を発表しました。

Eclipse MicroProfile
https://projects.eclipse.org/projects/technology.microprofile
MicroProfile GraphQL introduction
https://www.phillip-kruger.com/post/microprofile_graphql_introduction/
https://logico-jp.io/2020/04/07/microprofile-graphql-introduction/
GraphQL
https://spec.graphql.org/

このエントリでは、クライアント側にフォーカスして、Jakarta EEとMicroProfileの文脈で、Javaを使ってGraphQLエンドポイントと対話する方法をご紹介します。

Jakarta EE
https://jakarta.ee/

この記事で紹介しているソースコードはGitHubにあります。ご自由にお使いください。

A GraphQL client running on JakartaEE/MicroProfile using Nodes and Quarkus.
https://github.com/jefrajames/graphqlcli

Choosing a GraphQL Java client

MicroProfile GraphQL 1.0にはクライアントAPIは含まれていません。初版のリリースでは意図的にサーバーサイド、つまり、code-firstアプローチに従って、可能な限り簡単に適切にデータとオペレーションを公開する方法に注力ポイントをおいていたからです。

クライアントAPIは1.1でリリース予定です。クライアントAPIでは、2種類のフレーバー、つまり低レベルのものと高レベルのものを提供する予定にしています。

microprofile-graphql マイルストン
https://github.com/eclipse/microprofile-graphql/milestone/2

既存のREST仕様に例えると、低レベルAPIはJakarta RESTful Web Services (以前はJAX-RS)に似ており、高レベルAPIはMicroProfile Rest Clientに近いものになります。

Jakarta RESTful Web Services
https://projects.eclipse.org/projects/ee4j.jaxrs
MicroProfile Rest Client
https://github.com/eclipse/microprofile-rest-client

このプロジェクトでは、まず既存のGraphQL Javaクライアントを選択する必要があります。その中から3個を特定しました。

名称評価プロジェクト
Simple Java GraphQL clientGraphQLのオペレーション (クエリやmutation) を文字列として管理され、JSONレスポンスはラムダ関数によってデシリアライズされます。これは非常に低レベルですが、基本的な要件には十分かもしれません。Jakarta EE/MicroProfileとの親和性を高めるために、JSON-BとJSON-Pを使えるようにしました。A basic GraphQL client
https://github.com/jefrajames/basic-graphql-client
GraphQL APIs非常にハイレベルで、”single source of truth”としてGraphQL構文を使用する破壊的なアプローチを提案しています。マッピング専用の特定のJavaクラスを実装する必要はなく、GraphQLのオペレーションをコード化するだけで、バイトコードはIDEで自動生成されます(特定のIntelliJプラグインが必要です)。Manifold GraphQL evaluation
https://github.com/jefrajames/manival
NodesAmerican Expressによって開発されたJava GraphQLクライアントライブラリで、シンプルでありながら強力なAPIを提供しています。これを使い、GraphQLのオペレーションと結果とのマッピングを確実にするための特定のアノテーションを使ってクラスを開発します。Amex Nodes evaluation
https://github.com/jefrajames/evalnodes

この記事では、機能、利用の簡単さ、統合をうまく併せ持っているNodesの利用を提案します。

Exploring the person-example

Phillipの記事はGitHubにUpされているgraphql-exampleプロジェクトを使っています。実行方法は以下の通りです。

  1. GitHubリポジトリをフォークもしくはダウンロード
    A MicroProfile GraphQL Example
    https://github.com/phillip-kruger/graphql-example
  2. (Java 8より新しいバージョンではこのサンプルは動作しないため)JDKをJava 8に切り替える
  3. person-exampleに移動
  4. mvn thorntail:runを実行
  5. (GraphQLではなく)GraphiQLを使ってhttp://localhost:8080/graphiqlからGraphQLエンドポイントを操作

GraphiQLはJavaScriptベースのWeb GUIであり、これを使うと任意のGraphQLスキーマを発見し、GraphQLスキーマに対して操作を実行できます。左側のDocument Explorerペインでスキーマを閲覧できます。

ここで、QueryとMutationという名前の2個のルートクエリがあります。リンクを辿ることで、全ての公開済みのオペレーションとデータを見て回り、発見できます。

GraphQL vs REST

手短に言って、GraphQLはRESTとの違いはどこにあるのでしょうか。

  • スキーマから自動検出可能なしくみを備えています。OpenAPIのような追加のメタデータは必要ありません
  • 強い型付け:スキーマはデータとその型、そしてオペレーションを通じてそれらをどのように操作するのかを定義します。スキーマはサーバ(別名エンドポイント)とクライアント間の明示的な契約を定義します。
  • クライアントにとって非常に柔軟性が高い:各オペレーションに対し、クライアントは戻り値として返す選択フィールドを定義します。1個のアプリケーションで、同じエンティティをいくつでも表現できます。
  • 部分的な結果が可能:これはAll or Nothingのシナリオではありません。レスポンス内でエラーと部分的なデータの両方を取得できます。

だからといって、GraphQLがRESTより優れているということではありません。単純化しすぎではありますが、プロセス指向のAPIにはREST、データ指向のAPIにはGraphQLが良いと思います。そうは言っても、論争を始めたいわけではありません。両者は敵ではなく味方であり、どちらもアプリケーションで利用できるからです。

Using Nodes on Quarkus

We’re going to leverage Phillipのサンプルを活用し、Nodesクライアントのコーディングをしましょう。楽しくしかもリアルなものにするため、QuarkusでMicroProfileを使ってやってみましょう。

プロジェクトは2個のサービス(PersonとProfile)から構成されています。各々はEntity Control Boundaryモデルに従い3階層で構成されています。

バウンダリ層(boundary layer)完全にドキュメント化されたREST APIを公開
エンティティ層(entity layer)GraphQLのオペレーションや結果が利用するデータを保持
コントロール層(control layer)Node固有のAPIを使いGraphQLのオペレーションを実行

使い方は、SwaggerのGUI(http://localhost:9090/swagger-ui)に従うだけです。GraphQLの構文に慣れるために、オペレーションやレスポンスをコンソール上にINFOモードで書き出します。それらをコピーペーストして、GraphiQLで実行すればいいのです。

もちろん、現実には、中間のビジネスロジックを持たないこの類のRESTからGraphQLへの直接マッピングは意味がないでしょうが、あくまでも学習のためです。

Quarkus configuration

Quarkusの構成についてコメントしておきます。

  • person-exampleのバックエンドサービスのポート番号(デフォルトは8080)とかぶらないようにするために、Quarkusを9090で動作するように構成。
  • Swagger GUIを本番モードで有効にするため、quarkus.swagger-ui.always-includeをtrueに設定。
  • GraalVMを使うネイティブモードでアプリケーションを実行するため、イントロスペクション対象のクラスに @RegisterForReflection で注釈を付けなければならない。簡単にするため、エンティティパッケージ中の全てのクラスにこの注釈を付けた。
  • イントロスペクションのため、2個のNodesクラスを明示的に宣言しなければならない。コード変更を避けるため、プロジェクトのREADMEで説明したように構成ファイルで宣言する必要がある。

Coding with Nodes

想定通り、NodesはMicroProfileのコンテキスト、特に(JVM、ネイティブモードをと問わず)Quarkusと共に利用するのが簡単になりました。Nodesは統合を簡単にするJacksonに依存しているだけです。

Nodesのプログラミングモデルは強力に型付けされており、GraphQLのオペレーションや結果にマッピングするアノテーションの付いたJavaクラスに基づいています。例えばidを使ったpersonの抽出し、surname(姓)、names(名)、 birthDate(生年月日)、addresses(住所)のリストを取得するには以下のようにします。

// Other annotations to mentioned here
@GraphQLProperty(name = "person", arguments = {
    @GraphQLArgument(name = "personId")})
public class FindPersonByIdRequest {
    int id;
    String surname;
    String[] names;
    LocalDate birthDate;
    List<PersonByIdAddress> addresses;
}

慣れるまでには少し時間がかかりましたが、概念を掴めばうまくできるはずです。

プログラミングの観点から見て最も複雑な部分は、GraphQLが備え持つ柔軟性に対応することでした。例えば今回のコードでは、同じPersonエンティティの複数の表現があります。これはつまり同じビジネスエンティティを表す複数のJavaクラス(AddPersonRequest、DeletePersonRequest、PersonByIdRequestなど)がある、ということです。実プロジェクトでは、クラスの命名や、クラス間の整理や一貫性確保は課題になる可能性があります。クラスの拡散や矛盾のリスクを抑えるため、GraphQLの柔軟性とJavaの強力な型付けの間でトレードオフを見つけなければなりません。

Conclusion

この記事では、具体的にMicroProfile GraphQLアプリケーションを開発する方法を紹介してきました。

  • サーバーサイド:Phillip KrügerがMicroProfile GraphQL 1.0の説明に使ったサンプルを再利用
  • クライアントサイド:Nodesという、簡単に利用できる開発済みのJavaクライアントAPIを利用

では次は何をしましょうか。

まずは、MicroProfile GraphQLの実装がもっと増えると予想されます。今のところ、Phillipの例はThorntail上で動作していますが、OpenLiberty、Quarkus、WildFly、TomEE、Payara、KumuluzEEなどが実装を提案してくれることを期待しています。それと並行して、release 1.1でのGraphQLクライアントAPIに取り組んでいます。

1.1をご期待頂くと共に、GraphQLを使ってエンタープライスAPIを強化する準備をしておきましょう。この記事がお役に立つことを願っています。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中