Update docs for synchronous GraphQlClient

Closes gh-771
This commit is contained in:
rstoyanchev
2024-01-31 18:43:34 +00:00
parent 08d831aed3
commit 87aa98b1d8

View File

@@ -1,7 +1,7 @@
[[client]]
= Client
Spring for GraphQL includes client support for executing GraphQL requests over HTTP,
Spring for GraphQL includes client support to execute GraphQL requests over HTTP,
WebSocket, and RSocket.
@@ -9,22 +9,60 @@ WebSocket, and RSocket.
[[client.graphqlclient]]
== `GraphQlClient`
`GraphQlClient` is a contract that declares a common workflow for GraphQL requests that is
independent of the underlying transport. That means requests are executed with the same API
no matter what the underlying transport, and anything transport specific is configured at
build time.
`GraphQlClient` defines a common workflow for GraphQL requests independent of the underlying
transport, so the way you perform requests is the same no matter what transport is in use.
To create a `GraphQlClient` you need one of the following extensions:
The following transport specific `GraphQlClient` extensions are available:
- xref:client.adoc#client.httpsyncgraphqlclient[HttpSyncGraphQlClient]
- xref:client.adoc#client.httpgraphqlclient[HttpGraphQlClient]
- xref:client.adoc#client.websocketgraphqlclient[WebSocketGraphQlClient]
- xref:client.adoc#client.rsocketgraphqlclient[RSocketGraphQlClient]
Each defines a `Builder` with options relevant to the transport. All builders extend
from a common, base GraphQlClient xref:client.adoc#client.graphqlclient.builder[`Builder`] with options
relevant to all extensions.
from a common, base GraphQlClient xref:client.adoc#client.graphqlclient.builder[`Builder`]
with options applicable to all transports.
Once `GraphQlClient` is built you can begin to make xref:client.adoc#client.requests[requests].
[[client.httpsyncgraphqlclient]]
=== HTTP Sync
`HttpSyncGraphQlClient` uses
{spring-framework-ref-docs}/integration/rest-clients.html#rest-restclient[RestClient]
to execute GraphQL requests over HTTP through a blocking transport contract and chain of
interceptors.
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
----
Once `HttpSyncGraphQlClient` is created, you can begin to
xref:client.adoc#client.requests[execute requests] using the same API, independent of the underlying
transport. If you need to change any transport specific details, use `mutate()` on an
existing `HttpSyncGraphQlClient` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.builder(restClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpSyncGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
----
Once you have a `GraphQlClient` you can begin to make xref:client.adoc#client.requests[requests].
[[client.httpgraphqlclient]]
@@ -32,7 +70,8 @@ Once you have a `GraphQlClient` you can begin to make xref:client.adoc#client.re
`HttpGraphQlClient` uses
{spring-framework-ref-docs}/web/webflux-webclient.html[WebClient] to execute
GraphQL requests over HTTP.
GraphQL requests over HTTP through a non-blocking transport contract and chain of
interceptors.
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -195,12 +234,16 @@ transport.
[[client.graphqlclient.builder]]
=== Builder
`GraphQlClient` defines a parent `Builder` with common configuration options for the
`GraphQlClient` defines a parent `BaseBuilder` with common configuration options for the
builders of all extensions. Currently, it has lets you configure:
- `DocumentSource` strategy to load the document for a request from a file
- xref:client.adoc#client.interception[Interception] of executed requests
`BaseBuilder` is further extended by the following:
- `SyncBuilder` - blocking execution stack with a chain of ``SyncGraphQlInterceptor``'s.
- `Builder` - non-blocking execution stack with chain of ``GraphQlInterceptor``'s.
@@ -208,8 +251,8 @@ builders of all extensions. Currently, it has lets you configure:
== Requests
Once you have a xref:client.adoc#client.graphqlclient[`GraphQlClient`], you can begin to perform requests via
xref:client.adoc#client.requests.retrieve[retrieve()] or xref:client.adoc#client.requests.execute[execute()]
where the former is only a shortcut for the latter.
xref:client.adoc#client.requests.retrieve[retrieve] or xref:client.adoc#client.requests.execute[execute]
methods.
@@ -218,7 +261,29 @@ where the former is only a shortcut for the latter.
The below retrieves and decodes the data for a query:
[source,java,indent=0,subs="verbatim,quotes"]
[tabs]
======
Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Project project = graphQlClient.document(document) <1>
.retrieveSync("project") <2>
.toEntity(Project.class); <3>
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
String document = "{" +
" project(slug:\"spring-framework\") {" +
@@ -233,6 +298,8 @@ The below retrieves and decodes the data for a query:
.retrieve("project") <2>
.toEntity(Project.class); <3>
----
======
<1> The operation to perform.
<2> The path under the "data" key in the response map to decode from.
<3> Decode the data at the path to the target type.
@@ -249,7 +316,28 @@ Decoding can result in `FieldAccessException` if the given path is not present,
field value is `null` and has an error. `FieldAccessException` provides access to the
response and the field:
[source,java,indent=0,subs="verbatim,quotes"]
[tabs]
======
Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
try {
Project project = graphQlClient.document(document)
.retrieveSync("project")
.toEntity(Project.class);
}
catch (FieldAccessException ex) {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
}
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
@@ -261,6 +349,7 @@ response and the field:
// ...
});
----
======
@@ -272,9 +361,35 @@ response map. For more control, use the `execute` method and handle the response
For example:
[source,java,indent=0,subs="verbatim,quotes"]
[tabs]
======
Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
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>
}
}
Project project = field.toEntity(Project.class); <4>
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
Mono<Project> projectMono = graphQlClient.document(document)
.execute()
.map(response -> {
@@ -295,6 +410,8 @@ For example:
return field.toEntity(Project.class); <4>
});
----
======
<1> The response does not have data, only errors
<2> Field that is `null` and has an associated error
<3> Field that was set to `null` by its `DataFetcher`
@@ -331,9 +448,9 @@ You can then:
[source,java,indent=0,subs="verbatim,quotes"]
----
Mono<Project> projectMono = graphQlClient.documentName("projectReleases") <1>
Project project = graphQlClient.documentName("projectReleases") <1>
.variable("slug", "spring-framework") <2>
.retrieve()
.retrieveSync()
.toEntity(Project.class);
----
<1> Load the document from "projectReleases.graphql"
@@ -423,7 +540,23 @@ response directly:
[[client.interception]]
== Interception
You create a `GraphQlClientInterceptor` to intercept all requests through a client:
For blocking transports created with the `GraphQlClient.SyncBuilder`, you create a
`SyncGraphQlClientInterceptor` to intercept all requests through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
static class MyInterceptor implements SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
}
----
For non-blocking transports created with `GraphQlClient.Builder`, you create a
`GraphQlClientInterceptor` to intercept all requests through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -444,7 +577,7 @@ static class MyInterceptor implements GraphQlClientInterceptor {
}
----
Once the interceptor is created, register it through the client builder:
Once the interceptor is created, register it through the client builder. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----