SmallRye GraphQLクライアント
このガイドでは、QuarkusアプリケーションでGraphQLクライアントライブラリを使用する方法を説明します。このクライアントは、 SmallRye GraphQL プロジェクトによって実装されています。このガイドは特にクライアント側を対象としていますので、GraphQL全般の紹介が必要な場合は、まず SmallRye GraphQLガイドを参照してください。このガイドでは、GraphQLのクエリ言語、一般的なコンセプト、サーバーサイドの開発について紹介しています。
このガイドでは、サポートされている両方のタイプのGraphQLクライアントを使用して、リモートリソース(スターウォーズ関連のデータベース)からデータを取得するシンプルなアプリケーションの開発と実行について説明しています。このガイドは このウェブページで公開されているので、手動で試してみたい方はぜひご覧ください。Web UIでは、このアプリケーションに対してGraphQLクエリを書いて実行することができます。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
GraphQLクライアントタイプの紹介
2種類のGraphQLクライアントに対応しています。
typesafe クライアントは、GraphQLエンドポイントを呼び出すために調整されたMicroProfile REST Clientと非常によく似た動作をします。クライアントのインスタンスは基本的にプロキシであり、通常のJavaオブジェクトのように呼び出すことができますが、中では呼び出しがGraphQL操作に変換されます。クライアントはドメインクラスと直接連携します。操作のための入出力オブジェクトは、GraphQLのクエリ言語での表現に変換されます。
一方、 ダイナミック クライアントは、 jakarta.ws.rs.client
パッケージの Jakarta REST クライアントと同等に動作します。動作にドメイン クラスを必要とせず、代わりに GraphQL ドキュメントの抽象表現で動作します。ドキュメントは、ドメイン固有言語(DSL)を使用して構築されます。交換されたオブジェクトは抽象的な JsonObject
として扱われますが、必要に応じて(適切なモデルクラスが利用可能な場合)具体的なモデルオブジェクトに変換することが可能です。
タイプセーフ・クライアントは、使いやすさを重視したハイレベルで宣言的なアプローチと見ることができます。一方、ダイナミック・クライアントは、低レベルで命令的なアプローチであり、使い勝手はやや冗長ですが、操作や応答をより細かく制御することができます。
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは microprofile-graphql-client-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=microprofile-graphql-client-quickstart"
このコマンドはプロジェクトを生成し、 smallrye-graphql-client
エクステンションをインポートし、 rest-jsonb
エクステンションも使用します。
すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリで以下のコマンドを実行することで、
smallrye-graphql-client
エクステンションをプロジェクトに追加することができます。
quarkus extension add graphql-client
./mvnw quarkus:add-extension -Dextensions='graphql-client'
./gradlew addExtension --extensions='graphql-client'
これにより、ビルドファイルに以下が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql-client</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-graphql-client")
If you use JSON-B and JSON-P, make sure you don’t use the shortcut methods offered by At the moment, any single call to these methods will initialize a new You can import it as a static import to simplify your code:
|
Typesafeクライアントの使用
タイプセーフ・クライアントを使うには,スキーマと互換性のある対応するモデルクラスが必要です.それらを入手するには2つの方法があります。1つ目は、SmallRye GraphQLが提供するクライアントジェネレータを使用する方法で、これはスキーマドキュメントとクエリのリストからクラスを生成します。このジェネレーターは今のところ非常に実験的なものとされており、この例では取り上げていません。興味のある方は、 Client Generatorとそのドキュメントを参照してください。
この例では、必要なフィールドだけを持ち、必要のないものはすべて無視して、モデルクラスのスリムなバージョンを手動で作成します。必要なのは、 Film
と Planet
のクラスです。しかし、このサービスでは、 FilmConnection
と PlanetConnection
という名前の特定のラッパーも使用しており、今回の目的では、それぞれ Film
と Planet
のインスタンスの実際のリストを格納するだけです。
すべてのモデルクラスを作成して、 org.acme.microprofile.graphql.client.model
パッケージに入れましょう:
public class FilmConnection {
private List<Film> films;
public List<Film> getFilms() {
return films;
}
public void setFilms(List<Film> films) {
this.films = films;
}
}
public class Film {
private String title;
private PlanetConnection planetConnection;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public PlanetConnection getPlanetConnection() {
return planetConnection;
}
public void setPlanetConnection(PlanetConnection planetConnection) {
this.planetConnection = planetConnection;
}
}
public class PlanetConnection {
private List<Planet> planets;
public List<Planet> getPlanets() {
return planets;
}
public void setPlanets(List<Planet> planets) {
this.planets = planets;
}
}
public class Planet {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
モデルクラスができたので、次は、リモートのGraphQLサービスを呼び出すための実際の操作セットを表すインターフェースを作成します。
@GraphQLClientApi(configKey = "star-wars-typesafe")
public interface StarWarsClientApi {
FilmConnection allFilms();
}
わかりやすくするために、 allFilms
という名前のクエリだけを呼び出しています。対応するメソッドの名前も allFilms
としました。もしメソッドの名前が違っていたら、このメソッドが呼ばれたときに実行されるべきクエリの名前を指定するために、 @Query(value="allFilms")
をアノテーションする必要があります。
クライアントにも設定が必要で、少なくともリモートサービスのURLが必要です。これは、 @GraphQLClientApi
アノテーション内で指定する( endpoint
パラメータを設定する)か、設定ファイル( application.properties
)に移すことができます:
quarkus.smallrye-graphql-client.star-wars-typesafe.url=https://swapi-graphql.netlify.app/.netlify/functions/index
テストのみ の場合、URL はオプションのプロパティであり、指定されていない場合、Quarkus はクライアントのターゲットがテストされているアプリケーションであると推定します (通常、http://localhost:8081/graphql ) 。これは、アプリケーションに GraphQL サーバー側 API と、API のテストに使用される GraphQL クライアントが含まれている場合に役立ちます。
|
If you need to add an authorization header, or any other custom HTTP header (in our case it’s not required), this can be done with a configuration in the configuration file as well:
quarkus.smallrye-graphql-client.star-wars-typesafe.header.HEADER-KEY=HEADER-VALUE
star-wars-typesafe
は設定されたクライアントインスタンスの名前で、 @GraphQLClientApi
アノテーションの configKey
に対応しています。カスタム名を指定したくない場合は、 configKey
を省略して、インターフェイスの完全修飾名を使って参照することができます。
クライアントインスタンスが適切に設定されたので、アプリケーションの起動時にクライアントインスタンスに何かを実行させる方法が必要になります。そのために、RESTエンドポイントを使用します。ユーザーが呼び出すと、クライアントインスタンスを取得して、クエリを実行させます。
@Path("/")
public class StarWarsResource {
@Inject
StarWarsClientApi typesafeClient;
@GET
@Path("/typesafe")
@Produces(MediaType.APPLICATION_JSON)
@Blocking
public List<Film> getAllFilmsUsingTypesafeClient() {
return typesafeClient.allFilms().getFilms();
}
}
この REST エンドポイントをアプリケーションに組み込むと、 /typesafe
に GET リクエストを送信するだけで、アプリケーションは注入された typesafe クライアントインスタンスを使用してリモートサービスを呼び出し、映画と惑星を取得し、結果のリストの JSON 表現を返します。
ロギング
デバッグのために、 io.smallrye.graphql.client
カテゴリのログレベルを TRACE
に変更することで、typesafe クライアントによって生成されたリクエストと、サーバーによって送り返されたレスポンスをログに記録することができます(ロギングの設定方法の詳細については、 ロギングガイド を参照してください)。
これは、以下の行を application.properties
に追加することで実現できます:
quarkus.log.category."io.smallrye.graphql.client".level=TRACE
quarkus.log.category."io.smallrye.graphql.client".min-level=TRACE
dynamic クライアントの使用
ダイナミッククライアントでは、GraphQLのタイプやドキュメントの抽象的な表現を扱うことができるため、モデルクラスはオプションとなります。また、クライアントのAPIインターフェースは全く必要ありません。
クライアント用のURLを設定する必要がありますので、これを application.properties
に入れてみましょう:
quarkus.smallrye-graphql-client.star-wars-dynamic.url=https://swapi-graphql.netlify.app/.netlify/functions/index
このクライアントの名前を star-wars-dynamic
としました。動的なクライアントを注入する際には、注入ポイントを適切に特定するためにこの名前を使用します。
認証ヘッダやその他のカスタムHTTPヘッダ(ここでは必須ではありません)を追加する必要がある場合は、以下の方法で行います。
quarkus.smallrye-graphql-client.star-wars-dynamic.header.HEADER-KEY=HEADER-VALUE
これを先ほど作成した StarWarsResource
に追加します:
import static io.smallrye.graphql.client.core.Document.document;
import static io.smallrye.graphql.client.core.Field.field;
import static io.smallrye.graphql.client.core.Operation.operation;
// ....
@Inject
@GraphQLClient("star-wars-dynamic") (1)
DynamicGraphQLClient dynamicClient;
@GET
@Path("/dynamic")
@Produces(MediaType.APPLICATION_JSON)
@Blocking
public List<Film> getAllFilmsUsingDynamicClient() throws Exception {
Document query = document( (2)
operation(
field("allFilms",
field("films",
field("title"),
field("planetConnection",
field("planets",
field("name")
)
)
)
)
)
);
Response response = dynamicClient.executeSync(query); (3)
return response.getObject(FilmConnection.class, "allFilms").getFilms(); (4)
}
1 | インジェクションポイントを修飾することで、どの名前のクライアントをここに注入する必要があるかを知ることができます。 |
2 | ここでは、提供されているDSL言語を使って、GraphQLクエリを表すドキュメントを構築します。コードを読みやすくするために、静的なインポートを使用しています。このDSLは、GraphQLクエリを文字列で記述するのとよく似た方法で設計されています。 |
3 | クエリを実行し、応答を待つ間にブロックします。また、 Uni<Response> を返す非同期型もあります. |
4 | ここでは、利用可能なクラスがあるため、オプションのステップとして、レスポンスをモデルクラスのインスタンスに変換しています。利用可能なクラスがない場合や、利用したくない場合は、単に response.getData() を呼べば、返されたすべてのデータを表す JsonObject を得ることができます。 |
アプリケーションの実行
次のコマンドでアプリケーションを開発モードで起動します:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
クエリを実行するには、RESTエンドポイントにGETリクエストを送信する必要があります:
curl -s http://localhost:8080/dynamic # to use the dynamic client
curl -s http://localhost:8080/typesafe # to use the typesafe client
dynamic と typesafe のどちらを使用しても、結果は同じです。JSONドキュメントが読みにくい場合は、人間が読みやすいようにフォーマットしてくれるツールに通したほうがいいかもしれません。たとえば、出力を jq
に通してみてはいかがでしょうか。