Overview
Spring GraphQL provides support for Spring applications built on GraphQL Java. It is a joint collaboration between both teams. Our shared philosophy is to be less opinionated and more focused on comprehensive and wide-ranging support.
Spring GraphQL is the successor of the GraphQL Java Spring project from the GraphQL Java team. It aims to be the foundation for all Spring, GraphQL applications.
The project is in a milestone phase towards a 1.0 release, currently, and looking for feedback. Please, use our issue tracker to report a problem, discuss a design issue, or request a feature.
To get started, please see the Boot Starter and the Samples sections.
Web Transports
Spring GraphQL supports GraphQL requests over HTTP and over WebSocket.
HTTP
GraphQlHttpHandler
handles GraphQL over HTTP requests and delegates to the
Web 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 GraphQL request details included as JSON in the request body, as defined in the proposed 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.
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
Boot starter does this, see Web Endpoints for details or check
GraphQlWebMvcAutoConfiguration
or GraphQlWebFluxAutoConfiguration
for example config.
The Spring GraphQL repository contains a Spring MVC HTTP sample application.
WebSocket
GraphQlWebSocketHandler
handles GraphQL over WebSocket requests based on the
protocol defined in the
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 Web Interception chain for further
request execution.
GraphQL Over WebSocket Protocols
There are two such protocols, one in the subscriptions-transport-ws library and another in the graphql-ws library. The former is not active and succeeded by the latter. Read this 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
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. The Boot
starter has options to enable this, see Web Endpoints for details or check
GraphQlWebMvcAutoConfiguration
or GraphQlWebFluxAutoConfiguration
for example config.
The Spring GraphQL repository contains a WebFlux WebSocket sample application.
Web Interception
HTTP and WebSocket transport handlers delegate to a common Web
interception chain for request execution. The chain consists of a sequence of
WebInterceptor
components, followed by a GraphQlService
that invokes the GraphQL
Java engine.
WebInterceptor
is as a common contract to use in both Spring MVC and WebFlux
applications. Use it to intercept requests, inspect HTTP request headers, or to register a
transformation of the graphql.ExecutionInput
:
class MyInterceptor implements WebInterceptor {
@Override
public Mono<WebOutput> intercept(WebInput webInput, WebGraphQlHandler next) {
webInput.configureExecutionInput((executionInput, builder) -> {
Map<String, Object> map = ... ;
return builder.extensions(map).build();
});
return next.handle(webInput);
}
}
Use WebInterceptor
also to intercept responses, add HTTP response headers, or transform
the graphql.ExecutionResult
:
class MyInterceptor implements WebInterceptor {
@Override
public Mono<WebOutput> intercept(WebInput webInput, WebGraphQlHandler next) {
return next.handle(webInput)
.map(webOutput -> {
Object data = webOutput.getData();
Object updatedData = ... ;
return webOutput.transform(builder -> builder.data(updatedData));
});
}
}
WebGraphQlHandler
provides a builder to initialize the Web interception chain. After
you build the chain, you can use the resulting WebGraphQlHandler
to initialize the HTTP
or WebSocket transport handlers. The Boot starter configures all this,
see Web Endpoints for details, or check GraphQlWebMvcAutoConfiguration
or
GraphQlWebFluxAutoConfiguration
for example config.
Request Execution
GraphQlService
is the main Spring GraphQL abstraction to call GraphQL Java to execute
requests. Underlying transports, such as the Web Transports, delegate to GraphQlService
to
handle requests.
The main implementation, ExecutionGraphQlService
, is a thin facade around the
invocation of graphql.GraphQL
. It is configured with a GraphQlSource
for access to
the graphql.GraphQL
instance.
GraphQLSource
GraphQlSource
is a core Spring GraphQL abstraction for access to the
graphql.GraphQL
instance to use for request execution. It provides a builder API to
initialize GraphQL Java and build a GraphQlSource
.
The default GraphQlSource
builder, accessible via GraphQlSource.builder()
, enables
support for Reactive DataFetcher
, Context Propagation, and
Exception Resolution.
Reactive DataFetcher
The default GraphQlSource
builder enables support for a DataFetcher
to return Mono
or Flux
which adapts those to a CompletableFuture
where Flux
values are aggregated
and turned into a List, unless the request is a GraphQL subscription request,
in which case the return value remains a Reactive Streams Publisher
for streaming
GraphQL responses.
A reactive DataFetcher
can rely on access to Reactor context propagated from the
transport layer, such as from a WebFlux request handling, see
WebFlux Context.
Context Propagation
Spring GraphQL provides support to transparently propagate context from the Web Transports,
through the GraphQL engine, and to DataFetcher
and other components it invokes.
This includes both ThreadLocal
context from the Spring MVC request handling thread and
Reactor Context
from the WebFlux processing pipeline.
WebMvc
A DataFetcher
and other components invoked by GraphQL Java may not always execute on
the same thread as the Spring MVC handler, for example if an asynchronous
WebInterceptor
or DataFetcher
switches to a different thread.
Spring GraphQL supports propagating ThreadLocal
values from the Servlet container
thread to the thread a DataFetcher
and other components invoked by the GraphQL engine
execute on. To do this, an application needs to create a ThreadLocalAccessor
to extract
ThreadLocal
values of interest:
public class RequestAttributesAccessor implements ThreadLocalAccessor {
private static final String KEY = RequestAttributesAccessor.class.getName();
@Override
public void extractValues(Map<String, Object> container) {
container.put(KEY, RequestContextHolder.getRequestAttributes());
}
@Override
public void restoreValues(Map<String, Object> values) {
if (values.containsKey(KEY)) {
RequestContextHolder.setRequestAttributes((RequestAttributes) values.get(KEY));
}
}
@Override
public void resetValues(Map<String, Object> values) {
RequestContextHolder.resetRequestAttributes();
}
}
A ThreadLocalAccessor
can be registered in the WebGraphHandler
builder. The Boot starter detects beans of this type and automatically registers them for
Spring MVC application, see Web Endpoints.
WebFlux
A Reactive DataFetcher
can rely on access to Reactor context that
originates from the WebFlux request handling chain. This includes Reactor context
added by WebInterceptor components.
Exception Resolution
GraphQL Java applications can register a DataFetcherExceptionHandler
to decide how to
represent exceptions from the data layer in the "errors" section of the GraphQL response.
Spring GraphQL has a built-in DataFetcherExceptionHandler
that is configured for use
by the GraphQLSource
builder. It enables applications to register one or
more Spring DataFetcherExceptionResolver
components that are invoked sequentially
until one resolves the Exception
to a list of graphql.GraphQLError
objects.
DataFetcherExceptionResolver
is an asynchronous contract. For most implementations, it
would be sufficient to extend DataFetcherExceptionResolverAdapter
and override
one of its resolveToSingleError
or resolveToMultipleErrors
methods that
resolve exceptions synchronously.
A GraphQLError
can be assigned an graphql.ErrorClassification
. Spring GraphQL
defines an ErrorType
enum with common, error classification categories:
-
BAD_REQUEST
-
UNAUTHORIZED
-
FORBIDDEN
-
NOT_FOUND
-
INTERNAL_ERROR
Applications can use this to classify errors. If an error remains unresolved, by
default it is marked as INTERNAL_ERROR
.
Data Integration
Querydsl
Spring GraphQL supports use of Querydsl to fetch data through the Spring Data Querydsl extension. Querydsl provides a flexible yet typesafe approach to express query predicates by generating a meta-model using annotation processors.
For example, declare a repository as QuerydslPredicateExecutor
:
public interface AccountRepository extends Repository<Account, Long>,
QuerydslPredicateExecutor<Account> {
}
Then use it to create a DataFetcher
:
// For single result queries
DataFetcher<Account> dataFetcher =
QuerydslDataFetcher.builder(repository).single();
// For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher =
QuerydslDataFetcher.builder(repository).many();
The DataFetcher
builds a Querydsl Predicate
from GraphQL request parameters, and
uses it to fetch data. Spring Data supports QuerydslPredicateExecutor
for JPA,
MongoDB, and LDAP.
If the repository is ReactiveQuerydslPredicateExecutor
, the builder returns
DataFetcher<Mono<Account>>
or DataFetcher<Flux<Account>>
. Spring Data supports this
variant for MongoDB.
The webmvc-http sample in the Spring GraphQL repository
uses Querydsl to fetch artifactRepositories
.
Customizations
The Querydsl integration allows customizing the request parameters binding onto a
Predicate
by accepting a QuerydslBinderCustomizer
. Request parameters are bound
by default as "is equal to" for each available property in the request.
QuerydslDataFetcher
supports
interface and DTO projections
to transform query results before returning these for further GraphQL processing.
Auto Registration
QuerydslDataFetcher
exposes a GraphQLTypeVisitor
that finds top-level queries whose
return type matches the domain type of one or more Querydsl repositories, and registers
a DataFetcher
for each matching query. This includes both queries that return a single
value and queries that return a list of values.
The repository must be annotated with @GraphQlRepository
. By default, the name of the
GraphQL type returned by the query must match the simple name of the repository domain
type. Of if they don’t match, you can use the typeName
attribute of
@GraphQlRepository
to set the GraphQL type name.
Such repositories are auto-detected in the Boot starter.
Annotated Controllers
Spring GraphQL provides an annotation-based programming model where @Controller
components use annotations to declare handler methods with flexible method signatures to
fetch the data for specific GraphQL fields. For example:
@Controller
public class GreetingController {
@QueryMapping (1)
public String hello() { (2)
return "Hello, world!";
}
}
1 | Bind this method to a query, i.e. a field under the Query type. |
2 | Determine the query from the method name if not declared on the annotation. |
Spring GraphQL uses RuntimeWiring.Builder
to register the above handler method as a
graphql.schema.DataFetcher
for the query named "hello".
Declaration
You can define @Controller
beans as standard Spring bean definitions. The
@Controller
stereotype allows for auto-detection, aligned with Spring general
support for detecting @Controller
and @Component
classes on the classpath and
auto-registering bean definitions for them. It also acts as a stereotype for the annotated
class, indicating its role as a data fetching component in a GraphQL application.
AnnotatedDataFetcherConfigurer
detects @Controller
beans and registers their
annotated handler methods as DataFetcher
s via RuntimeWiring.Builder
. It is an
implementation of RuntimeWiringConfigurer
which can be added to GraphQlSource.Builder
.
The Spring Boot starter automatically declares AnnotatedDataFetcherConfigurer
as a bean
and adds all RuntimeWiringConfigurer
beans to GraphQlSource.Builder
and that enables
support for annotated DataFetcher
s, see RuntimeWiring.
Mapping
The @SchemaMapping
annotation maps a handler method to a field in the GraphQL schema
and declares it to be the DataFetcher
for that field. The annotation can specify the
parent type name, and the field name:
@Controller
public class BookController {
@SchemaMapping(typeName="Book", field="author")
public Author getAuthor(Book book) {
// ...
}
}
The @SchemaMapping
annotation can also leave out those attributes, in which case the
field name defaults to the method name, while the type name defaults to the simple class
name of the source/parent object injected into the method. For example, the below
defaults to type "Book" and field "author":
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
The @SchemaMapping
annotation can be declared at the class level to specify a default
type name for all handler methods in the class.
@Controller
@SchemaMapping(typeName="Book")
public class BookController {
// @SchemaMapping methods for fields of the "Book" type
}
@QueryMapping
, @MutationMapping
, and @SubscriptionMapping
are meta annotations that
are themselves annotated with @SchemaMapping
and have the typeName preset to Query
,
Mutation
, or Subscription
respectively. Effectively, these are shortcut annotations
for fields under the Query, Mutation, and Subscription types respectively. For example:
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInput bookInput) {
// ...
}
@SubscriptionMapping
public Flux<Book> newPublications() {
// ...
}
}
Handler Methods
@SchemaMapping
handler methods have flexible signatures and can choose from a range of
method arguments and return values..
Method Arguments
Annotated handler methods can choose from one of the following method arguments:
Method Argument | Description |
---|---|
|
For access to field arguments with conversion.
See |
Source |
For access to the source (i.e. parent/container) instance of the field. See Source. |
|
For direct access to the underlying |
|
For access to the context from the |
Return Values
Annotated handler methods can return any value, including Reactor Mono
and Flux
as
described in Reactive DataFetcher
.
@Argument
In GraphQL Java, the DataFetchingEnvironment
provides access to field-specific argument
values. The arguments are available as simple scalar values such as String, or as a Map
of values for more complex input, or a List
of values.
Use @Argument
to access an argument for the field that maps to the handler method. You
can declare such a method parameter to be of any type. If necessary, Spring GraphQL
converts the value by serializing it to JSON first and then to the target type.
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInput bookInput) {
// ...
}
}
You can explicitly specify the argument name, for example @Argument("bookInput")
, or if
it not specified, it defaults to the method parameter name, but this requires the
-parameters
compiler flag with Java 8+ or debugging information from the compiler.
By default, an @Argument
is required, but you can make it optional by setting the
required
flag to false or by declaring the argument with java.util.Optional
.
You can use @Argument
on a Map<String, Object>
argument, to obtain all argument
values. The name attribute on @Argument
must not be set.
Source
In GraphQL Java, the DataFetchingEnvironment
provides access to the source (i.e.
parent/container) instance of the field. To access this, simply declare a method parameter
of the expected target type.
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
The source method argument also helps to determine the type name for the mapping.
If the simple name of the Java class matches the GraphQL type, then there is no need to
explicitly specify the type name in the @SchemaMapping
annotation.
Security
The path to a Web GraphQL endpoint can be secured with HTTP URL security to ensure that only authenticated users can access it. This does not, however, differentiate among different GraphQL requests on such a shared endpoint on a single URL.
To apply more fine-grained security, add Spring Security annotations such as
@PreAuthorize
or @Secured
to service methods involved in fetching specific parts of
the GraphQL response. This should work due to Context Propagation that aims to make
Security, and other context, available at the data fetching level.
The Spring GraphQL repository contains samples for Spring MVC and for WebFlux.
Testing
You can test GraphQL requests using Spring’s WebTestClient
, just send and receive
JSON, but a number of GraphQL specific details make this approach more cumbersome than it
should be.
GraphQlTester
GraphQlTester
defines a workflow to test GraphQL requests with the following benefits:
-
Verify GraphQL responses are 200 (OK).
-
Verify no unexpected errors under the "errors" key in the response.
-
Decode under the "data" key in the response.
-
Use JsonPath to decode different parts of the response.
-
Test subscriptions.
To create GraphQlTester
, you only need a GraphQlService
, and no transport:
GraphQlSource graphQlSource = GraphQlSource.builder()
.schemaResources(...)
.runtimeWiringConfigurer(...)
.build();
GraphQlService graphQlService = new ExecutionGraphQlService(graphQlSource);
GraphQlTester graphQlTester = GraphQlTester.builder(graphQlService).build();
WebGraphQlTester
WebGraphQlTester
extends GraphQlTester
to add a workflow and configuration specific
to Web Transports. You need one of the following inputs to create it:
-
WebTestClient
— perform requests as an HTTP client, either against HTTP handlers without a server, or against a live server. -
WebGraphQlHandler
— perform requests through the Web Interception chain used by both HTTP and WebSocket handlers, which in effect is testing without a Web framework. One reason to use this is for Subscriptions.
For Spring WebFlux without a server, you can point to your Spring configuration:
ApplicationContext context = ... ;
WebTestClient client =
WebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
For Spring MVC without a server, use the MockMvcWebTestClient
builder:
WebApplicationContext context = ... ;
WebTestClient client =
MockMvcWebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
For tests against a live, running server:
WebTestClient client =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
Queries
Below is an example query test using JsonPath to extract all release versions in the GraphQL response.
String query = "{" +
" project(slug:\"spring-framework\") {" +
" releases {" +
" version" +
" }"+
" }" +
"}";
graphQlTester.query(query)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
The JsonPath is relative to the "data" section of the response.
Errors
Tests cannot use verify data, if there are errors under the "errors" key in the response
has errors. If necessary to ignore an error, use an error filter Predicate
:
graphQlTester.query(query)
.execute()
.errors()
.filter(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
An error filter can be registered globally and apply to all tests:
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
.errorFilter(error -> ...)
.build();
Or inspect all errors directly and that also marks them as filtered:
graphQlTester.query(query)
.execute()
.errors()
.satisfy(errors -> {
// ...
});
If a request does not have any response data (e.g. mutation), use executeAndVerify
instead of execute
to verify there are no errors in the response:
graphQlTester.query(query).executeAndVerify();
Subscriptions
The executeSubscription
method defines a workflow specific to subscriptions which return
a stream of responses instead of a single response.
To test subscriptions, you can create GraphQlTester
with a GraphQlService
, which
calls graphql.GraphQL
directly and that returns a stream of responses:
GraphQlService service = ... ;
GraphQlTester graphQlTester = GraphQlTester.builder(service).build();
Flux<String> result = graphQlTester.query("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class); // decode each response
The StepVerifier
from Project Reactor is useful to verify a stream:
Flux<String> result = graphQlTester.query("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class);
StepVerifier.create(result)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
To test with the Web Interception chain, you can create WebGraphQlTester
with a
WebGraphQlHandler
:
GraphQlService service = ... ;
WebGraphQlHandler handler = WebGraphQlHandler.builder(service)
.interceptor((input, next) -> next.handle(input))
.build();
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(handler).build();
Currently, Spring GraphQL does not support testing with a WebSocket client, and it cannot be used for integration test of GraphQL over WebSocket requests.
Boot Starter
This projects builds on Boot 2.5.x, but it should be compatible with the latest Boot 2.4.x.
Project Setup
To create a project, go to https://start.spring.io and select starter(s) for the GraphQL transports you want to use:
Starter | Transport | Implementation |
---|---|---|
|
HTTP |
Spring MVC |
|
WebSocket |
WebSocket for Servlet apps |
|
HTTP, WebSocket |
Spring WebFlux |
In the generated project, add graphql-spring-boot-starter
manually:
dependencies {
// Spring GraphQL Boot starter
implementation 'org.springframework.experimental:graphql-spring-boot-starter:1.0.0-SNAPSHOT'
// ...
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' } // Spring milestones
maven { url 'https://repo.spring.io/snapshot' } // Spring snapshots
}
<dependencies>
// Spring GraphQL Boot starter
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- ... -->
</dependencies>
<!-- For Spring project milestones or snapshot releases -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Boot Starter Group Id
The Boot starter will move from the Spring GraphQL repository to the Spring Boot
repository, after Spring Boot 2.6 is released. The group id for the starter will then
change from |
Schema
By default, GraphQL schema files are expected to be in src/main/resources/graphql
and have
the extension ".graphqls", ".graphql", ".gql", or ".gqls". You can customize the
schema locations to check as follows:
spring.graphql.schema.locations=classpath:graphql/
The GraphQL schema can be viewed over HTTP at "/graphql/schema". This is not enabled by default:
spring.graphql.schema.printer.enabled=false
RuntimeWiring
The GraphQL Java RuntimeWiring.Builder
can be used to register DataFetcher
s,
type resolvers, custom scalar types, and more. You can declare RuntimeWiringConfigurer
beans in your Spring config to get access to the RuntimeWiring.Builder
. The Boot
starter detects such beans adds them to GraphQlSource.Builder.
Typically, however, applications will not implement DataFetcher
directly and will
instead create annotated controllers. The Boot
starter declares a RuntimeWiringConfigurer
called AnnotatedDataFetcherConfigurer
that
detects @Controller
classes with annotated handler methods and registers those as
DataFetcher
s.
Querydsl Repositories
Spring Data repositories that extend QuerydslPredicateExecutor
or
ReactiveQuerydslPredicateExecutor
and are annotated with @GraphQlRepository
are
detected and considered as candidates for DataFetcher
auto registration for matching top-level queries.
Web Endpoints
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized:
spring.graphql.path=/graphql
The GraphQL WebSocket endpoint supports WebSocket handshakes at "/graphql" by default. The below shows the properties that apply for WebSocket handling:
spring.graphql.websocket.path=/graphql
# Time within which a "CONNECTION_INIT" message must be received from the client
spring.graphql.websocket.connection-init-timeout=60s
The GraphQL WebSocket endpoint is off by default. To enable it:
-
For a Servlet application, add the WebSocket starter
spring-boot-starter-websocket
. -
For a WebFlux application, set the
spring.graphql.websocket.path
application property.
Declare a WebInterceptor
bean to have it registered in the
Web Interception for GraphQL over HTTP and WebSocket
requests.
Declare a ThreadLocalAccessor
bean to assist with the propagation of ThreadLocal
values of interest in Spring MVC.
GraphiQL
The Spring Boot starter includes a GraphiQL page that is exposed at "/graphiql" by default. You can configure this as follows:
spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql
Metrics
When the starter spring-boot-starter-actuator
is present on the classpath, metrics for
GraphQL requests are collected. You can disable metrics collection as follows:
management.metrics.graphql.autotime.enabled=false
Metrics can be exposed with an Actuator web endpoint. The following sections assume that its exposure is enabled in your application configuration, as follows:
management.endpoints.web.exposure.include=health,metrics,info
Request Timer
A Request metric timer is available at /actuator/metrics/graphql.request
.
Tag | Description | Sample values |
---|---|---|
outcome |
Request outcome |
"SUCCESS", "ERROR" |
Testing
For Spring GraphQL testing support, add the below to your classpath and that will make
a WebGraphQlTester
available for injection into tests:
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.graphql:spring-graphql-test:1.0.0-SNAPSHOT'
// Also add this, unless spring-boot-starter-webflux
is also present
testImplementation 'org.springframework:spring-webflux'
// ...
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' } // Spring milestones
maven { url 'https://repo.spring.io/snapshot' } // Spring snapshots
}
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<!-- Also add this, unless "spring-boot-starter-webflux" is also present -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<!-- ... -->
</dependencies>
<!-- For Spring project milestones or snapshot releases -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
For GraphQL over HTTP with Spring MVC, using MockMvc
as the server:
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureGraphQlTester
public class MockMvcGraphQlTests {
@Autowired
private WebGraphQlTester graphQlTester;
}
For GraphQL over HTTP with Spring WebFlux, using a mock server:
@SpringBootTest
@AutoConfigureWebTestClient
@AutoConfigureGraphQlTester
public class MockMvcGraphQlTests {
@Autowired
private WebGraphQlTester graphQlTester;
}
For GraphQL over HTTP with a running server:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureGraphQlTester
public class MockMvcGraphQlTests {
@Autowired
private WebGraphQlTester graphQlTester;
}
Subscriptions can be tested without WebSocket as shown below:
@SpringBootTest
@AutoConfigureGraphQlTester
public class MockMvcGraphQlTests {
@Autowired
private WebGraphQlTester graphQlTester;
@Test
void subscription() {
Flux<String> result = this.graphQlTester.query("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class);
// Use StepVerifier from "reactor-test" to verify the stream...
StepVerifier.create(result)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
}
}
The above subscription test is performed directly against the WebGraphQlHandler
that
both HTTP and WebSocket transports delegate to. It passes through the WebInterceptor
chain and then calls GraphQL Java which returns a Reactive Streams Publisher
.
Samples
This Spring GraphQL repository contains sample applications for various scenarios.
You can run those by cloning this repository and running main application classes from your IDE or by typing the following on the command line:
$ ./gradlew :samples:{sample-directory-name}:bootRun