1
1
[[client]]
2
2
= Client
3
3
4
- Spring for GraphQL includes client support for executing GraphQL requests over HTTP,
4
+ Spring for GraphQL includes client support to execute GraphQL requests over HTTP,
5
5
WebSocket, and RSocket.
6
6
7
7
8
8
9
9
[[client.graphqlclient]]
10
10
== `GraphQlClient`
11
11
12
- `GraphQlClient` is a contract that declares a common workflow for GraphQL requests that is
13
- independent of the underlying transport. That means requests are executed with the same API
14
- no matter what the underlying transport, and anything transport specific is configured at
15
- build time.
12
+ `GraphQlClient` defines a common workflow for GraphQL requests independent of the underlying
13
+ transport, so the way you perform requests is the same no matter what transport is in use.
16
14
17
- To create a `GraphQlClient` you need one of the following extensions :
15
+ The following transport specific `GraphQlClient` extensions are available :
18
16
17
+ - xref:client.adoc#client.httpsyncgraphqlclient[HttpSyncGraphQlClient]
19
18
- xref:client.adoc#client.httpgraphqlclient[HttpGraphQlClient]
20
19
- xref:client.adoc#client.websocketgraphqlclient[WebSocketGraphQlClient]
21
20
- xref:client.adoc#client.rsocketgraphqlclient[RSocketGraphQlClient]
22
21
23
22
Each defines a `Builder` with options relevant to the transport. All builders extend
24
- from a common, base GraphQlClient xref:client.adoc#client.graphqlclient.builder[`Builder`] with options
25
- relevant to all extensions.
23
+ from a common, base GraphQlClient xref:client.adoc#client.graphqlclient.builder[`Builder`]
24
+ with options applicable to all transports.
25
+
26
+ Once `GraphQlClient` is built you can begin to make xref:client.adoc#client.requests[requests].
27
+
28
+
29
+ [[client.httpsyncgraphqlclient]]
30
+ === HTTP Sync
31
+
32
+ `HttpSyncGraphQlClient` uses
33
+ {spring-framework-ref-docs}/integration/rest-clients.html#rest-restclient[RestClient]
34
+ to execute GraphQL requests over HTTP through a blocking transport contract and chain of
35
+ interceptors.
36
+
37
+ [source,java,indent=0,subs="verbatim,quotes"]
38
+ ----
39
+ RestClient restClient = ... ;
40
+ HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
41
+ ----
42
+
43
+ Once `HttpSyncGraphQlClient` is created, you can begin to
44
+ xref:client.adoc#client.requests[execute requests] using the same API, independent of the underlying
45
+ transport. If you need to change any transport specific details, use `mutate()` on an
46
+ existing `HttpSyncGraphQlClient` to create a new instance with customized settings:
47
+
48
+ [source,java,indent=0,subs="verbatim,quotes"]
49
+ ----
50
+ RestClient restClient = ... ;
51
+
52
+ HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.builder(restClient)
53
+ .headers(headers -> headers.setBasicAuth("joe", "..."))
54
+ .build();
55
+
56
+ // Perform requests with graphQlClient...
57
+
58
+ HttpSyncGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
59
+ .headers(headers -> headers.setBasicAuth("peter", "..."))
60
+ .build();
61
+
62
+ // Perform requests with anotherGraphQlClient...
63
+
64
+ ----
26
65
27
- Once you have a `GraphQlClient` you can begin to make xref:client.adoc#client.requests[requests].
28
66
29
67
30
68
[[client.httpgraphqlclient]]
31
69
=== HTTP
32
70
33
71
`HttpGraphQlClient` uses
34
72
{spring-framework-ref-docs}/web/webflux-webclient.html[WebClient] to execute
35
- GraphQL requests over HTTP.
73
+ GraphQL requests over HTTP through a non-blocking transport contract and chain of
74
+ interceptors.
36
75
37
76
[source,java,indent=0,subs="verbatim,quotes"]
38
77
----
@@ -195,21 +234,25 @@ transport.
195
234
[[client.graphqlclient.builder]]
196
235
=== Builder
197
236
198
- `GraphQlClient` defines a parent `Builder ` with common configuration options for the
237
+ `GraphQlClient` defines a parent `BaseBuilder ` with common configuration options for the
199
238
builders of all extensions. Currently, it has lets you configure:
200
239
201
240
- `DocumentSource` strategy to load the document for a request from a file
202
241
- xref:client.adoc#client.interception[Interception] of executed requests
203
242
243
+ `BaseBuilder` is further extended by the following:
244
+
245
+ - `SyncBuilder` - blocking execution stack with a chain of ``SyncGraphQlInterceptor``'s.
246
+ - `Builder` - non-blocking execution stack with chain of ``GraphQlInterceptor``'s.
204
247
205
248
206
249
207
250
[[client.requests]]
208
251
== Requests
209
252
210
253
Once you have a xref:client.adoc#client.graphqlclient[`GraphQlClient`], you can begin to perform requests via
211
- xref:client.adoc#client.requests.retrieve[retrieve() ] or xref:client.adoc#client.requests.execute[execute() ]
212
- where the former is only a shortcut for the latter .
254
+ xref:client.adoc#client.requests.retrieve[retrieve] or xref:client.adoc#client.requests.execute[execute]
255
+ methods .
213
256
214
257
215
258
@@ -218,7 +261,29 @@ where the former is only a shortcut for the latter.
218
261
219
262
The below retrieves and decodes the data for a query:
220
263
221
- [source,java,indent=0,subs="verbatim,quotes"]
264
+ [tabs]
265
+ ======
266
+ Sync::
267
+ +
268
+ [source,java,indent=0,subs="verbatim,quotes",role="primary"]
269
+ ----
270
+ String document = "{" +
271
+ " project(slug:\"spring-framework\") {" +
272
+ " name" +
273
+ " releases {" +
274
+ " version" +
275
+ " }"+
276
+ " }" +
277
+ "}";
278
+
279
+ Project project = graphQlClient.document(document) <1>
280
+ .retrieveSync("project") <2>
281
+ .toEntity(Project.class); <3>
282
+ ----
283
+
284
+ Non-Blocking::
285
+ +
286
+ [source,java,indent=0,subs="verbatim,quotes",role="secondary"]
222
287
----
223
288
String document = "{" +
224
289
" project(slug:\"spring-framework\") {" +
@@ -233,6 +298,8 @@ The below retrieves and decodes the data for a query:
233
298
.retrieve("project") <2>
234
299
.toEntity(Project.class); <3>
235
300
----
301
+ ======
302
+
236
303
<1> The operation to perform.
237
304
<2> The path under the "data" key in the response map to decode from.
238
305
<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,
249
316
field value is `null` and has an error. `FieldAccessException` provides access to the
250
317
response and the field:
251
318
252
- [source,java,indent=0,subs="verbatim,quotes"]
319
+ [tabs]
320
+ ======
321
+ Sync::
322
+ +
323
+ [source,java,indent=0,subs="verbatim,quotes",role="primary"]
324
+ ----
325
+ try {
326
+ Project project = graphQlClient.document(document)
327
+ .retrieveSync("project")
328
+ .toEntity(Project.class);
329
+ }
330
+ catch (FieldAccessException ex) {
331
+ ClientGraphQlResponse response = ex.getResponse();
332
+ // ...
333
+ ClientResponseField field = ex.getField();
334
+ // ...
335
+ }
336
+ ----
337
+
338
+ Non-Blocking::
339
+ +
340
+ [source,java,indent=0,subs="verbatim,quotes",role="secondary"]
253
341
----
254
342
Mono<Project> projectMono = graphQlClient.document(document)
255
343
.retrieve("project")
@@ -261,6 +349,7 @@ response and the field:
261
349
// ...
262
350
});
263
351
----
352
+ ======
264
353
265
354
266
355
@@ -272,9 +361,35 @@ response map. For more control, use the `execute` method and handle the response
272
361
273
362
For example:
274
363
275
- [source,java,indent=0,subs="verbatim,quotes"]
364
+ [tabs]
365
+ ======
366
+ Sync::
367
+ +
368
+ [source,java,indent=0,subs="verbatim,quotes",role="primary"]
369
+ ----
370
+ ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
371
+
372
+ if (!response.isValid()) {
373
+ // Request failure... <1>
374
+ }
375
+
376
+ ClientResponseField field = response.field("project");
377
+ if (!field.hasValue()) {
378
+ if (field.getError() != null) {
379
+ // Field failure... <2>
380
+ }
381
+ else {
382
+ // Optional field set to null... <3>
383
+ }
384
+ }
385
+
386
+ Project project = field.toEntity(Project.class); <4>
276
387
----
277
388
389
+ Non-Blocking::
390
+ +
391
+ [source,java,indent=0,subs="verbatim,quotes",role="secondary"]
392
+ ----
278
393
Mono<Project> projectMono = graphQlClient.document(document)
279
394
.execute()
280
395
.map(response -> {
@@ -295,6 +410,8 @@ For example:
295
410
return field.toEntity(Project.class); <4>
296
411
});
297
412
----
413
+ ======
414
+
298
415
<1> The response does not have data, only errors
299
416
<2> Field that is `null` and has an associated error
300
417
<3> Field that was set to `null` by its `DataFetcher`
@@ -331,9 +448,9 @@ You can then:
331
448
332
449
[source,java,indent=0,subs="verbatim,quotes"]
333
450
----
334
- Mono< Project> projectMono = graphQlClient.documentName("projectReleases") <1>
451
+ Project project = graphQlClient.documentName("projectReleases") <1>
335
452
.variable("slug", "spring-framework") <2>
336
- .retrieve ()
453
+ .retrieveSync ()
337
454
.toEntity(Project.class);
338
455
----
339
456
<1> Load the document from "projectReleases.graphql"
@@ -423,7 +540,23 @@ response directly:
423
540
[[client.interception]]
424
541
== Interception
425
542
426
- You create a `GraphQlClientInterceptor` to intercept all requests through a client:
543
+ For blocking transports created with the `GraphQlClient.SyncBuilder`, you create a
544
+ `SyncGraphQlClientInterceptor` to intercept all requests through the client:
545
+
546
+ [source,java,indent=0,subs="verbatim,quotes"]
547
+ ----
548
+ static class MyInterceptor implements SyncGraphQlClientInterceptor {
549
+
550
+ @Override
551
+ public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
552
+ // ...
553
+ return chain.next(request);
554
+ }
555
+ }
556
+ ----
557
+
558
+ For non-blocking transports created with `GraphQlClient.Builder`, you create a
559
+ `GraphQlClientInterceptor` to intercept all requests through the client:
427
560
428
561
[source,java,indent=0,subs="verbatim,quotes"]
429
562
----
@@ -444,7 +577,7 @@ static class MyInterceptor implements GraphQlClientInterceptor {
444
577
}
445
578
----
446
579
447
- Once the interceptor is created, register it through the client builder:
580
+ Once the interceptor is created, register it through the client builder. For example :
448
581
449
582
[source,java,indent=0,subs="verbatim,quotes"]
450
583
----
0 commit comments