@@ -155,47 +155,59 @@ public class GraphQlRSocketController {
155
155
[[server-interception]]
156
156
=== Interception
157
157
158
- Transport handlers for <<server-http>> and <<server-websocket>> delegate to a
159
- `WebGraphQlInterceptor` chain with an `ExecutionGraphQlService` at the end which calls
160
- the GraphQL Java engine. Use this to access HTTP request details and customize the
161
- `ExecutionInput` for GraphQL Java.
158
+ Server transports allow intercepting requests before and after the GraphQL Java engine is
159
+ called to process a request.
162
160
163
- For example, to extract HTTP request values and pass them to data fetchers:
161
+
162
+ [[server-interception-web]]
163
+ ==== `WebGraphQlInterceptor`
164
+
165
+ <<server-http>> and <<server-websocket>> transports invoke a chain of
166
+ 0 or more `WebGraphQlInterceptor`, followed by an `ExecutionGraphQlService` that calls
167
+ the GraphQL Java engine. `WebGraphQlInterceptor` allows an application to intercept
168
+ incoming requests and do one of the following:
169
+
170
+ - Check HTTP request details
171
+ - Customize the `graphql.ExecutionInput`
172
+ - Add HTTP response headers
173
+ - Customize the `graphql.ExecutionResult`
174
+
175
+ For example, an interceptor can pass an HTTP request header to a `DataFetcher`:
164
176
165
177
[source,java,indent=0,subs="verbatim,quotes"]
166
178
----
167
- class HeaderInterceptor implements WebGraphQlInterceptor {
179
+ class HeaderInterceptor implements WebGraphQlInterceptor { <1>
168
180
169
181
@Override
170
182
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
171
- List< String> values = request.getHeaders().get("headerName ");
183
+ String value = request.getHeaders().getFirst("myHeader ");
172
184
request.configureExecutionInput((executionInput, builder) ->
173
- builder.graphQLContext(Collections.singletonMap("headerName ", values )).build());
185
+ builder.graphQLContext(Collections.singletonMap("myHeader ", value )).build());
174
186
return chain.next(request);
175
187
}
176
188
}
177
189
178
- // Subsequent access from a controller
179
-
180
190
@Controller
181
- class MyController {
191
+ class MyController { <2>
182
192
183
193
@QueryMapping
184
194
Person person(@ContextValue String myHeader) {
185
195
// ...
186
196
}
187
197
}
188
198
----
199
+ <1> Interceptor adds HTTP request header value into GraphQLContext
200
+ <2> Data controller method accesses the value
189
201
190
- Or reversely, add values to the `GraphQLContext` and use them to update the HTTP response :
202
+ Reversely, an interceptor can access values added to the `GraphQLContext` by a controller :
191
203
192
204
[source,java,indent=0,subs="verbatim,quotes"]
193
205
----
194
206
@Controller
195
207
class MyController {
196
208
197
209
@QueryMapping
198
- Person person(GraphQLContext context) {
210
+ Person person(GraphQLContext context) { <1>
199
211
context.put("cookieName", "123");
200
212
}
201
213
}
@@ -205,7 +217,7 @@ class MyController {
205
217
class HeaderInterceptor implements WebGraphQlInterceptor {
206
218
207
219
@Override
208
- public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
220
+ public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) { <2>
209
221
return chain.next(request).doOnNext(response -> {
210
222
String value = response.getExecutionInput().getGraphQLContext().get("cookieName");
211
223
ResponseCookie cookie = ResponseCookie.from("cookieName", value).build();
@@ -214,13 +226,52 @@ class HeaderInterceptor implements WebGraphQlInterceptor {
214
226
}
215
227
}
216
228
----
229
+ <1> Controller adds value to the `GraphQLContext`
230
+ <2> Interceptor uses the value to add an HTTP response header
231
+
232
+ `WebGraphQlHandler` can modify the `ExecutionResult`, for example, to inspect and modify
233
+ request validation errors that are raised before execution begins and which cannot be
234
+ handled with a `DataFetcherExceptionResolver`:
217
235
218
- The `WebGraphQlInterceptor` chain can be updated through the `WebGraphQlHandler` builder,
219
- and the Boot starter uses this, see Boot's section on
236
+ [source,java,indent=0,subs="verbatim,quotes"]
237
+ ----
238
+ static class RequestErrorInterceptor implements WebGraphQlInterceptor {
239
+
240
+ @Override
241
+ public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
242
+ return chain.next(request).map(response -> {
243
+ if (response.isValid()) {
244
+ return response; <1>
245
+ }
246
+
247
+ List<GraphQLError> errors = response.getErrors().stream() <2>
248
+ .map(error -> {
249
+ GraphqlErrorBuilder<?> builder = GraphqlErrorBuilder.newError();
250
+ // ...
251
+ return builder.build();
252
+ })
253
+ .collect(Collectors.toList());
254
+
255
+ return response.transform(builder -> builder.errors(errors).build()); <3>
256
+ });
257
+ }
258
+ }
259
+ ----
260
+ <1> Return the same if `ExecutionResult` has a "data" key with non-null value
261
+ <2> Check and transform the GraphQL errors
262
+ <3> Update the `ExecutionResult` with the modified errors
263
+
264
+ Use `WebGraphQlHandler` to configure the `WebGraphQlInterceptor` chain. This is supported
265
+ by the Boot starter, see
220
266
{spring-boot-ref-docs}/web.html#web.graphql.transports.http-websocket[Web Endpoints].
221
267
222
- The <<server-rsocket>> transport handler delegates to a similar `GraphQlInterceptor`
223
- chain that you can use to intercept GraphQL over RSocket requests.
268
+
269
+ [[server-interception-rsocket]]
270
+ ==== `RSocketQlInterceptor`
271
+
272
+ Similar to <<server-interception-web>>, an `RSocketQlInterceptor` allows intercepting
273
+ GraphQL over RSocket requests before and after GraphQL Java engine execution. You can use
274
+ this to customize the `graphql.ExecutionInput` and the `graphql.ExecutionResult`.
224
275
225
276
226
277
@@ -567,6 +618,19 @@ error details.
567
618
Unresolved exception are logged at ERROR level along with the `executionId` to correlate
568
619
to the error sent to the client. Resolved exceptions are logged at DEBUG level.
569
620
621
+ [[execution-exceptions-request]]
622
+ ==== Request Exceptions
623
+
624
+ The GraphQL Java engine may run into validation or other errors when parsing the request
625
+ and that in turn prevent request execution. In such cases, the response contains a
626
+ "data" key with `null` and one or more request-level "errors" that are global, i.e. not
627
+ having a field path.
628
+
629
+ `DataFetcherExceptionResolver` cannot handle such global errors because they are raised
630
+ before execution begins and before any `DataFetcher` is invoked. An application can use
631
+ transport level interceptors to inspect and transform errors in the `ExecutionResult`.
632
+ See examples under <<server-interception-web>>.
633
+
570
634
571
635
[[execution-exceptions-subsctiption]]
572
636
==== Subscription Exceptions
0 commit comments