The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

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プロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:

コマンドラインインタフェース
quarkus create app org.acme:microprofile-graphql-client-quickstart \
    --extension='rest-jsonb,graphql-client' \
    --no-code
cd microprofile-graphql-client-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.17.2:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=microprofile-graphql-client-quickstart \
    -Dextensions='rest-jsonb,graphql-client' \
    -DnoCode
cd microprofile-graphql-client-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=microprofile-graphql-client-quickstart"

このコマンドはプロジェクトを生成し、 smallrye-graphql-client エクステンションをインポートし、 rest-jsonb エクステンションも使用します。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリで以下のコマンドを実行することで、 smallrye-graphql-client エクステンションをプロジェクトに追加することができます。

コマンドラインインタフェース
quarkus extension add graphql-client
Maven
./mvnw quarkus:add-extension -Dextensions='graphql-client'
Gradle
./gradlew addExtension --extensions='graphql-client'

これにより、ビルドファイルに以下が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-graphql-client</artifactId>
</dependency>
build.gradle
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 jakarta.json.Json such as Json.createValue(…​).

At the moment, any single call to these methods will initialize a new JsonProvider, which is extremely slow. Quarkus provides a shared JsonProvider instance via the JsonProviderHolder class of the quarkus-jsonp extension.

You can import it as a static import to simplify your code:

import static io.quarkus.jsonp.JsonProviderHolder.jsonProvider;

[...]

    public void method() {
        jsonProvider().createValue("value");
    }

[...]

アプリケーション

今回作成するアプリケーションでは、どちらのタイプのGraphQLクライアントも使用します。どちらの場合も、 SWAPIのスター・ウォーズサービスに接続し、スター・ウォーズの映画のリストと、各映画に登場する惑星の名前を問い合わせます。

これに対応するGraphQLクエリは次のようになります:

{
  allFilms {
    films {
      title
      planetConnection {
        planets {
          name
        }
      }
    }
  }
}

このクエリを手動で実行するには、 Webページにアクセスしてください。

Typesafeクライアントの使用

タイプセーフ・クライアントを使うには,スキーマと互換性のある対応するモデルクラスが必要です.それらを入手するには2つの方法があります。1つ目は、SmallRye GraphQLが提供するクライアントジェネレータを使用する方法で、これはスキーマドキュメントとクエリのリストからクラスを生成します。このジェネレーターは今のところ非常に実験的なものとされており、この例では取り上げていません。興味のある方は、 Client Generatorとそのドキュメントを参照してください。

この例では、必要なフィールドだけを持ち、必要のないものはすべて無視して、モデルクラスのスリムなバージョンを手動で作成します。必要なのは、 FilmPlanet のクラスです。しかし、このサービスでは、 FilmConnectionPlanetConnection という名前の特定のラッパーも使用しており、今回の目的では、それぞれ FilmPlanet のインスタンスの実際のリストを格納するだけです。

すべてのモデルクラスを作成して、 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
Maven
./mvnw quarkus:dev
Gradle
./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 に通してみてはいかがでしょうか。

まとめ

この例では、ダイナミックなGraphQLクライアントとタイプセーフなGraphQLクライアントの両方を使って外部のGraphQLサービスを呼び出す方法を示し、クライアントの種類の違いを説明しました。

関連コンテンツ

On the same topics