The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

SmallRye GraphQLクライアント

This guide demonstrates how your Quarkus application can use the GraphQL client library. The client is implemented by the SmallRye GraphQL project. This guide is specifically geared towards the client side, so if you need an introduction to GraphQL in general, first refer to the SmallRye GraphQL guide, which provides an introduction to the GraphQL query language, general concepts and server-side development.

このガイドでは、サポートされている両方のタイプのGraphQLクライアントを使用して、リモートリソース(スターウォーズ関連のデータベース)からデータを取得するシンプルなアプリケーションの開発と実行について説明しています。このガイドは このウェブページで公開されているので、手動で試してみたい方はぜひご覧ください。Web UIでは、このアプリケーションに対してGraphQLクエリを書いて実行することができます。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 11+ がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.8.1+

  • 使用したい場合、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

GraphQLクライアントタイプの紹介

2種類のGraphQLクライアントに対応しています。

typesafe クライアントは、GraphQLエンドポイントを呼び出すために調整されたMicroProfile REST Clientと非常によく似た動作をします。クライアントのインスタンスは基本的にプロキシであり、通常のJavaオブジェクトのように呼び出すことができますが、中では呼び出しがGraphQL操作に変換されます。クライアントはドメインクラスと直接連携します。操作のための入出力オブジェクトは、GraphQLのクエリ言語での表現に変換されます。

一方、 ダイナミック ・クライアントは、 javax.ws.rs.client パッケージの JAX-RS クライアントに相当するものとして動作する。このクライアントは、ドメインクラスを必要とせず、代わりにGraphQLドキュメントの抽象的な表現を使って動作する。ドキュメントは、ドメイン固有の言語(DSL)を使って構築される。交換されたオブジェクトは、抽象的な JsonObject として扱われるが、必要に応じて、(適切なモデルクラスが利用可能であれば)具体的なモデルオブジェクトに変換することができる。

タイプセーフ・クライアントは、使いやすさを重視したハイレベルで宣言的なアプローチと見ることができます。一方、ダイナミック・クライアントは、低レベルで命令的なアプローチであり、使い勝手はやや冗長ですが、操作や応答をより細かく制御することができます。

ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

このソリューションは microprofile-graphql-client-quickstart ディレクトリにあります。

Mavenプロジェクトの作成

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

CLI
quarkus create app org.acme:microprofile-graphql-client-quickstart \
    --extension=resteasy-reactive-jsonb,graphql-client,rest-client-reactive \
    --no-code
cd microprofile-graphql-client-quickstart

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

Quarkus CLIのインストール方法については、Quarkus CLIガイドをご参照ください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=microprofile-graphql-client-quickstart \
    -Dextensions="resteasy-reactive-jsonb,graphql-client,rest-client-reactive" \
    -DnoCode
cd microprofile-graphql-client-quickstart

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

The typesafe GraphQL client depends on REST client, thus we included the rest-client-reactive extension in the extensions list. You may also switch to the traditional non-reactive rest-client if the rest of your application depends on the non-reactive RESTEasy stack (you can’t mix reactive and non-reactive RESTEasy). If you’re only going to use the dynamic GraphQL client and don’t use RESTEasy in your application, you may leave out the REST client dependency completely. This command generates a project, importing the smallrye-graphql-client extension.

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

CLI
quarkus extension add 'graphql-client,rest-client-reactive'
Maven
./mvnw quarkus:add-extension -Dextensions="graphql-client,rest-client-reactive"
Gradle
./gradlew addExtension --extensions="graphql-client,rest-client-reactive"

繰り返しになりますが、ダイナミッククライアントのみを使用する場合は、 rest-client-reactive を省いても構いません。

This will add the following to your build file:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-graphql-client</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-client-reactive</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-smallrye-graphql-client")
implementation("io.quarkus:quarkus-rest-client-reactive")

アプリケーション

今回作成するアプリケーションでは、どちらのタイプの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") をアノテーションする必要があります。

The client also needs some configuration, namely at least the URL of the remote service. We can either specify that within the @GraphQLClientApi annotation (by setting the endpoint parameter), or move this over to the configuration file, application.properties:

quarkus.smallrye-graphql-client.star-wars-typesafe.url=https://swapi-graphql.netlify.app/.netlify/functions/index

star-wars-typesafe is the name of the configured client instance, and corresponds to the configKey in the @GraphQLClientApi annotation. If you don’t want to specify a custom name, you can leave out the configKey, and then refer to it by using the fully qualified name of the interface.

Now that we have the client instance properly configured, we need a way to have it perform something when we start the application. For that, we will use a REST endpoint that, when called by a user, obtains the client instance and lets it execute the query.

@Path("/")
public class StarWarsResource {
    @Inject
    StarWarsClientApi typesafeClient;

    @GET
    @Path("/typesafe")
    @Produces(MediaType.APPLICATION_JSON)
    @Blocking
    public List<Film> getAllFilmsUsingTypesafeClient() {
        return typesafeClient.allFilms().getFilms();
    }
}

With this REST endpoint included in your application, you can simply send a GET request to /typesafe, and the application will use an injected typesafe client instance to call the remote service, obtain the films and planets, and return the JSON representation of the resulting list.

ダイナミッククライアントの使用

ダイナミッククライアントでは、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 Here we build a document representing the GraphQL query, using the provided DSL language. We use static imports to make the code easier to read. The DSL is designed in a way that it looks quite similar to writing a GraphQL query as a string.
3 クエリを実行し、応答を待つ間にブロックします。また、 Uni<Response> を返す非同期型もあります.
4 Here we did the optional step of converting the response to instances of our model classes, because we have the classes available. If you don’t have the classes available or don’t want to use them, simply calling response.getData() would get you a JsonObject representing all the returned data.

アプリケーションの実行

Launch the application in dev mode using:

CLI
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サービスを呼び出す方法を示し、クライアントの種類の違いを説明しました。