最新の安定バージョンについては、Spring GraphQL 1.3.1 を使用してください! |
クライアント
Spring for GraphQL には、HTTP、WebSocket、RSocket を介して GraphQL リクエストを実行するためのクライアントサポートが含まれています。
GraphQlClient
GraphQlClient
は、基になるトランスポートから独立した GraphQL リクエストの共通ワークフローを宣言する契約です。つまり、基になるトランスポートが何であっても、リクエストは同じ API で実行され、トランスポート固有のものはビルド時に構成されます。
GraphQlClient
を作成するには、次の拡張のいずれかが必要です。
それぞれが、トランスポートに関連するオプションを持つ Builder
を定義します。すべてのビルダーは、共通のベース GraphQlClient Builder
から拡張され、すべての拡張に関連するオプションがあります。
GraphQlClient
を取得したら、リクエストを開始できます。
HTTP
HttpGraphQlClient
は WebClient を使用して、HTTP 経由で GraphQL リクエストを実行します。
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);
HttpGraphQlClient
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。トランスポート固有の詳細を変更する必要がある場合は、既存の HttpGraphQlClient
で mutate()
を使用して、カスタマイズされた設定で新しいインスタンスを作成します。
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.builder(webClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
WebSocket
WebSocketGraphQlClient
は、共有 WebSocket 接続を介して GraphQL リクエストを実行します。これは Spring WebFlux の WebSocketClient を使用して構築されており、次のように作成できます。
String url = "wss://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();
HttpGraphQlClient
とは対照的に、WebSocketGraphQlClient
は接続指向です。つまり、リクエストを行う前に接続を確立する必要があります。リクエストを開始すると、接続が透過的に確立されます。または、クライアントの start()
メソッドを使用して、リクエストの前に明示的に接続を確立します。
接続指向であることに加えて、WebSocketGraphQlClient
は多重化もされています。すべてのリクエストに対して単一の共有接続を維持します。接続が失われた場合は、次のリクエストで、または start()
が再度呼び出された場合に再確立されます。進行中のリクエストをキャンセルし、接続を閉じ、新しいリクエストを拒否するクライアントの stop()
メソッドを使用することもできます。
そのサーバーへのすべてのリクエストに対して単一の共有接続を確立するには、サーバーごとに単一の WebSocketGraphQlClient インスタンスを使用します。各クライアントインスタンスは独自の接続を確立しますが、これは通常、単一のサーバーを対象とするものではありません。 |
WebSocketGraphQlClient
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。トランスポート固有の詳細を変更する必要がある場合は、既存の WebSocketGraphQlClient
で mutate()
を使用して、カスタマイズされた設定で新しいインスタンスを作成します。
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use graphQlClient...
WebSocketGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherGraphQlClient...
インターセプター
WebSocket 上の GraphQL [GitHub] (英語) プロトコルは、実行リクエストに加えて、多数の接続指向メッセージを定義します。例: クライアントは "connection_init"
を送信し、サーバーは接続の開始時に "connection_ack"
で応答します。
WebSocket トランスポート固有のインターセプトの場合、WebSocketGraphQlClientInterceptor
を作成できます。
static class MyInterceptor implements WebSocketGraphQlClientInterceptor {
@Override
public Mono<Object> connectionInitPayload() {
// ... the "connection_init" payload to send
}
@Override
public Mono<Void> handleConnectionAck(Map<String, Object> ackPayload) {
// ... the "connection_ack" payload received
}
}
上記のインターセプターを他の GraphQlClientInterceptor
と同様に登録し、それを GraphQL リクエストのインターセプトにも使用しますが、型 WebSocketGraphQlClientInterceptor
のインターセプターは最大で 1 つしか存在できないことに注意してください。
RSocket
RSocketGraphQlClient
は RSocketRequester を使用して、RSocket リクエストを介して GraphQL リクエストを実行します。
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlClient client = RSocketGraphQlClient.builder()
.clientTransport(transport)
.build();
HttpGraphQlClient
とは対照的に、RSocketGraphQlClient
は接続指向です。つまり、リクエストを行う前にセッションを確立する必要があります。リクエストを開始すると、セッションは透過的に確立されます。または、クライアントの start()
メソッドを使用して、リクエストの前に明示的にセッションを確立します。
RSocketGraphQlClient
も多重化されています。すべてのリクエストに対して単一の共有セッションを維持します。セッションが失われた場合、次のリクエストで、start()
が再度呼び出された場合に再確立されます。進行中のリクエストをキャンセルし、セッションを閉じ、新しいリクエストを拒否するクライアントの stop()
メソッドを使用することもできます。
そのサーバーへのすべてのリクエストに対して単一の共有セッションを使用するには、サーバーごとに単一の RSocketGraphQlClient インスタンスを使用します。各クライアントインスタンスは独自の接続を確立しますが、これは通常、単一のサーバーを対象とするものではありません。 |
RSocketGraphQlClient
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。
ビルダー
GraphQlClient
は、すべての拡張機能のビルダーに共通の構成オプションを持つ親 Builder
を定義します。現在、以下を構成できます。
ファイルからリクエストのドキュメントをロードする
DocumentSource
戦略実行されたリクエストのインターセプト
要求
GraphQlClient
を取得したら、retrieve() または execute() を介してリクエストの実行を開始できます。前者は後者のショートカットにすぎません。
取得
以下は、クエリのデータを取得してデコードします。
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Mono<Project> projectMono = graphQlClient.document(document) (1)
.retrieve("project") (2)
.toEntity(Project.class); (3)
1 | 実行する操作。 |
2 | デコード元のレスポンスマップの「データ」キーのパス。 |
3 | ターゲット型へのパスでデータをデコードします。 |
入力ドキュメントは String
であり、リテラルまたはコード生成されたリクエストオブジェクトによって生成されます。ファイルでドキュメントを定義し、ドキュメントソースを使用してファイル名でドキュメントを再発行することもできます。
パスは「データ」キーに相対的であり、ネストされたフィールドには単純なドット ( "." ) で区切られた表記法を使用し、リスト要素のオプションの配列インデックスを使用します。"project.name"
または "project.releases[0].version"
。
指定されたパスが存在しない場合、またはフィールド値が null
でエラーがある場合、デコードすると FieldAccessException
になる可能性があります。FieldAccessException
は、レスポンスとフィールドへのアクセスを提供します。
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
.toEntity(Project.class)
.onErrorResume(FieldAccessException.class, ex -> {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
});
実行
取得は、レスポンスマップ内の単一のパスからデコードするためのショートカットにすぎません。さらに制御するには、execute
メソッドを使用してレスポンスを処理します。
例:
Mono<Project> projectMono = graphQlClient.document(document)
.execute()
.map(response -> {
if (!response.isValid()) {
// Request failure... (1)
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure... (2)
}
else {
// Optional field set to null... (3)
}
}
return field.toEntity(Project.class); (4)
});
1 | レスポンスにはデータがなく、エラーのみがあります |
2 | null で、関連するエラーがあるフィールド |
3 | DataFetcher によって null に設定されたフィールド |
4 | 指定されたパスでデータをデコードします |
ドキュメントソース
リクエストのドキュメントは String
であり、ローカル変数または定数で定義されるか、コード生成されたリクエストオブジェクトによって生成されます。
クラスパス上の "graphql-documents/"
に、拡張子 .graphql
または .gql
のドキュメントファイルを作成し、ファイル名で参照することもできます。
例: src/main/resources/graphql-documents
内の projectReleases.graphql
というファイルが与えられた場合、内容は次のとおりです。
query projectReleases($slug: ID!) {
project(slug: $slug) {
name
releases {
version
}
}
}
その後、次のことができます。
Mono<Project> projectMono = graphQlClient.documentName("projectReleases") (1)
.variable("slug", "spring-framework") (2)
.retrieve()
.toEntity(Project.class);
1 | "projectReleases.graphql" からドキュメントを読み込みます |
2 | 変数値を提供します。 |
IntelliJ の "JS GraphQL" プラグインは、コード補完 で GraphQL クエリファイルをサポートします。
GraphQlClient
ビルダーを使用して、名前でドキュメントをロードするために DocumentSource
をカスタマイズできます。
サブスクリプションリクエスト
GraphQlClient
は、それをサポートするトランスポート上でサブスクリプションを実行できます。WebSocket および RSocket トランスポートのみが GraphQL サブスクリプションをサポートしているため、WebSocketGraphQlClient または RSocketGraphQlClient を作成する必要があります。
取得
サブスクリプションストリームを開始するには、retrieveSubscription
を使用します。これは、単一のレスポンスを取得するのに似ていますが、それぞれが何らかのデータにデコードされたレスポンスのストリームを返します。
Flux<String> greetingFlux = client.document("subscription { greetings }")
.retrieveSubscription("greeting")
.toEntity(String.class);
サブスクリプションがサーバー側から「エラー」メッセージで終了した場合、Flux
は SubscriptionErrorException
で終了する可能性があります。この例外により、「エラー」メッセージからデコードされた GraphQL エラーへのアクセスが提供されます。
基礎となる接続が閉じられるか失われた場合、Flux
は WebSocketDisconnectedException
などの GraphQlTransportException
で終了することがあります。その場合、retry
オペレーターを使用してサブスクリプションを再開できます。
クライアント側からサブスクリプションを終了するには、Flux
をキャンセルする必要があり、次に WebSocket トランスポートが「完了」メッセージをサーバーに送信します。Flux
の解約方法は使い方によって異なります。take
や timeout
などの一部のオペレーターは、それ自体で Flux
をキャンセルします。Subscriber
とともに Flux
をサブスクライブしている場合は、Subscription
への参照を取得し、それを通じてキャンセルできます。onSubscribe
オペレーターは、Subscription
へのアクセスも提供します。
実行
取得は、各レスポンスマップ内の単一のパスからデコードするためのショートカットにすぎません。さらに制御するには、executeSubscription
メソッドを使用して、各レスポンスを直接処理します。
Flux<String> greetingFlux = client.document("subscription { greetings }")
.executeSubscription()
.map(response -> {
if (!response.isValid()) {
// Request failure...
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure...
}
else {
// Optional field set to null... (3)
}
}
return field.toEntity(String.class)
});
インターセプト
クライアントを介してすべてのリクエストをインターセプトする GraphQlClientInterceptor
を作成します。
static class MyInterceptor implements GraphQlClientInterceptor {
@Override
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
@Override
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
// ...
return chain.next(request);
}
}
インターセプターが作成されたら、クライアントビルダーを使用して登録します。
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.interceptor(new MyInterceptor())
.build();