165 lines
7.4 KiB
Plaintext
165 lines
7.4 KiB
Plaintext
[[server.transports]]
|
|
= Server Transports
|
|
|
|
Spring for GraphQL supports server handling of GraphQL requests over HTTP, WebSocket, and
|
|
RSocket.
|
|
|
|
|
|
[[server.transports.http]]
|
|
== HTTP
|
|
|
|
`GraphQlHttpHandler` handles GraphQL over HTTP requests and delegates to the
|
|
xref:transports.adoc#server.interception[Interception] chain for request execution. There are two variants, one for
|
|
Spring MVC and one for Spring WebFlux. Both handle requests asynchronously and have
|
|
equivalent functionality, but rely on blocking vs non-blocking I/O respectively for
|
|
writing the HTTP response.
|
|
|
|
Requests must use HTTP POST with `"application/json"` as content type and GraphQL request details
|
|
included as JSON in the request body, as defined in the proposed
|
|
https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md[GraphQL over HTTP] specification.
|
|
Once the JSON body has been successfully decoded, the HTTP response status is always 200 (OK),
|
|
and any errors from GraphQL request execution appear in the "errors" section of the GraphQL response.
|
|
The default and preferred choice of media type is `"application/graphql-response+json"`, but `"application/json"`
|
|
is also supported, as described in the specification.
|
|
|
|
`GraphQlHttpHandler` can be exposed as an HTTP endpoint by declaring a `RouterFunction`
|
|
bean and using the `RouterFunctions` from Spring MVC or WebFlux to create the route. The
|
|
xref:boot-starter.adoc[Boot Starter] does this, see the
|
|
{spring-boot-ref-docs}/web.html#web.graphql.transports.http-websocket[Web Endpoints] section for
|
|
details, or check `GraphQlWebMvcAutoConfiguration` or `GraphQlWebFluxAutoConfiguration`
|
|
it contains, for the actual config.
|
|
|
|
The 1.0.x branch of this repository contains a Spring MVC
|
|
{github-10x-branch}/samples/webmvc-http[HTTP sample] application.
|
|
|
|
[[server.transports.http.fileupload]]
|
|
=== File Upload
|
|
|
|
As a protocol GraphQL focuses on the exchange of textual data. This doesn't include binary
|
|
data such as images, but there is a separate, informal
|
|
https://github.com/jaydenseric/graphql-multipart-request-spec[graphql-multipart-request-spec]
|
|
that allows file uploads with GraphQL over HTTP.
|
|
|
|
Spring for GraphQL does not support the `graphql-multipart-request-spec` directly.
|
|
While the spec does provide the benefit of a unified GraphQL API, the actual experince has
|
|
led to a number of issues, and best practice recommendations have evolved, see
|
|
https://www.apollographql.com/blog/backend/file-uploads/file-upload-best-practices/[Apollo Server File Upload Best Practices]
|
|
for a more detailed discussion.
|
|
|
|
If you would like to use `graphql-multipart-request-spec` in your application, you can
|
|
do so through the library
|
|
https://github.com/nkonev/multipart-spring-graphql[multipart-spring-graphql].
|
|
|
|
|
|
[[server.transports.websocket]]
|
|
== WebSocket
|
|
|
|
`GraphQlWebSocketHandler` handles GraphQL over WebSocket requests based on the
|
|
https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md[protocol] defined in the
|
|
https://github.com/enisdenjo/graphql-ws[graphql-ws] library. The main reason to use
|
|
GraphQL over WebSocket is subscriptions which allow sending a stream of GraphQL
|
|
responses, but it can also be used for regular queries with a single response.
|
|
The handler delegates every request to the xref:transports.adoc#server.interception[Interception] chain for further
|
|
request execution.
|
|
|
|
[TIP]
|
|
.GraphQL Over WebSocket Protocols
|
|
====
|
|
There are two such protocols, one in the
|
|
https://github.com/apollographql/subscriptions-transport-ws[subscriptions-transport-ws]
|
|
library and another in the
|
|
https://github.com/enisdenjo/graphql-ws[graphql-ws] library. The former is not active and
|
|
succeeded by the latter. Read this
|
|
https://the-guild.dev/blog/graphql-over-websockets[blog post] for the history.
|
|
====
|
|
|
|
There are two variants of `GraphQlWebSocketHandler`, one for Spring MVC and one for
|
|
Spring WebFlux. Both handle requests asynchronously and have equivalent functionality.
|
|
The WebFlux handler also uses non-blocking I/O and back pressure to stream messages,
|
|
which works well since in GraphQL Java a subscription response is a Reactive Streams
|
|
`Publisher`.
|
|
|
|
The `graphql-ws` project lists a number of
|
|
https://github.com/enisdenjo/graphql-ws#recipes[recipes] for client use.
|
|
|
|
`GraphQlWebSocketHandler` can be exposed as a WebSocket endpoint by declaring a
|
|
`SimpleUrlHandlerMapping` bean and using it to map the handler to a URL path. By default,
|
|
the xref:boot-starter.adoc[Boot Starter] does not expose a GraphQL over WebSocket endpoint, but it's easy to
|
|
enable it by adding a property for the endpoint path. Please, see the
|
|
{spring-boot-ref-docs}/web.html#web.graphql.transports.http-websocket[Web Endpoints]
|
|
section for details, or check the `GraphQlWebMvcAutoConfiguration` or the
|
|
`GraphQlWebFluxAutoConfiguration` for the actual Boot starter config.
|
|
|
|
The 1.0.x branch of this repository contains a WebFlux
|
|
{github-10x-branch}/samples/webflux-websocket[WebSocket sample] application.
|
|
|
|
|
|
|
|
[[server.transports.rsocket]]
|
|
== RSocket
|
|
|
|
`GraphQlRSocketHandler` handles GraphQL over RSocket requests. Queries and mutations are
|
|
expected and handled as an RSocket `request-response` interaction while subscriptions are
|
|
handled as `request-stream`.
|
|
|
|
`GraphQlRSocketHandler` can be used a delegate from an `@Controller` that is mapped to
|
|
the route for GraphQL requests. For example:
|
|
|
|
include-code::GraphQlRSocketController[]
|
|
|
|
|
|
|
|
|
|
[[server.interception]]
|
|
== Interception
|
|
|
|
Server transports allow intercepting requests before and after the GraphQL Java engine is
|
|
called to process a request.
|
|
|
|
|
|
[[server.interception.web]]
|
|
=== `WebGraphQlInterceptor`
|
|
|
|
xref:transports.adoc#server.transports.http[HTTP] and xref:transports.adoc#server.transports.websocket[WebSocket] transports invoke a chain of
|
|
0 or more `WebGraphQlInterceptor`, followed by an `ExecutionGraphQlService` that calls
|
|
the GraphQL Java engine. `WebGraphQlInterceptor` allows an application to intercept
|
|
incoming requests and do one of the following:
|
|
|
|
- Check HTTP request details
|
|
- Customize the `graphql.ExecutionInput`
|
|
- Add HTTP response headers
|
|
- Customize the `graphql.ExecutionResult`
|
|
|
|
For example, an interceptor can pass an HTTP request header to a `DataFetcher`:
|
|
|
|
include-code::RequestHeaderInterceptor[]
|
|
<1> Interceptor adds HTTP request header value into GraphQLContext
|
|
<2> Data controller method accesses the value
|
|
|
|
Reversely, an interceptor can access values added to the `GraphQLContext` by a controller:
|
|
|
|
include-code::ResponseHeaderInterceptor[]
|
|
<1> Controller adds value to the `GraphQLContext`
|
|
<2> Interceptor uses the value to add an HTTP response header
|
|
|
|
`WebGraphQlHandler` can modify the `ExecutionResult`, for example, to inspect and modify
|
|
request validation errors that are raised before execution begins and which cannot be
|
|
handled with a `DataFetcherExceptionResolver`:
|
|
|
|
include-code::RequestErrorInterceptor[]
|
|
<1> Return the same if `ExecutionResult` has a "data" key with non-null value
|
|
<2> Check and transform the GraphQL errors
|
|
<3> Update the `ExecutionResult` with the modified errors
|
|
|
|
Use `WebGraphQlHandler` to configure the `WebGraphQlInterceptor` chain. This is supported
|
|
by the xref:boot-starter.adoc[Boot Starter], see
|
|
{spring-boot-ref-docs}/web.html#web.graphql.transports.http-websocket[Web Endpoints].
|
|
|
|
|
|
[[server.interception.rsocket]]
|
|
=== `RSocketQlInterceptor`
|
|
|
|
Similar to xref:transports.adoc#server.interception.web[`WebGraphQlInterceptor`], an `RSocketQlInterceptor` allows intercepting
|
|
GraphQL over RSocket requests before and after GraphQL Java engine execution. You can use
|
|
this to customize the `graphql.ExecutionInput` and the `graphql.ExecutionResult`.
|