458 lines
12 KiB
Plaintext
458 lines
12 KiB
Plaintext
[[boot-graphql]]
|
|
= Boot Starter
|
|
|
|
This projects builds on Boot 2.5.x, but it should be compatible with the latest Boot 2.4.x.
|
|
|
|
|
|
|
|
[[boot-graphql-project]]
|
|
== Project Setup
|
|
|
|
To create a project, go to https://start.spring.io and select starter(s) for the
|
|
GraphQL transports you want to use:
|
|
|
|
[cols="1,1,1"]
|
|
|===
|
|
| Starter | Transport | Implementation
|
|
|
|
| `spring-boot-starter-web`
|
|
| HTTP
|
|
| Spring MVC
|
|
|
|
| `spring-boot-starter-websocket`
|
|
| WebSocket
|
|
| WebSocket for Servlet apps
|
|
|
|
| `spring-boot-starter-webflux`
|
|
| HTTP, WebSocket
|
|
| Spring WebFlux
|
|
|
|
|===
|
|
|
|
In the generated project, add `graphql-spring-boot-starter` manually:
|
|
|
|
[source,groovy,indent=0,subs="verbatim,quotes",role="primary"]
|
|
.Gradle
|
|
----
|
|
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
|
|
}
|
|
----
|
|
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
.Maven
|
|
----
|
|
<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>
|
|
----
|
|
|
|
[NOTE]
|
|
.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 `org.springframework.experimental` to `org.springframework.boot` and will be
|
|
released in Spring Boot 2.7.
|
|
====
|
|
|
|
|
|
|
|
[[boot-graphql-schema]]
|
|
== 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:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
spring.graphql.schema.locations=classpath:graphql/
|
|
----
|
|
|
|
The GraphQL schema can be viewed over HTTP at "/graphql/schema". This is not enabled by
|
|
default:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
spring.graphql.schema.printer.enabled=false
|
|
----
|
|
|
|
|
|
[[boot-graphql-runtimewiring]]
|
|
== 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 <<execution-graphqlsource,GraphQlSource.Builder>>.
|
|
|
|
Typically, however, applications will not implement ``DataFetcher`` directly and will
|
|
instead create <<controllers,annotated controllers>>. The Boot
|
|
starter declares a `RuntimeWiringConfigurer` called `AnnotatedDataFetcherConfigurer` that
|
|
detects `@Controller` classes with annotated handler methods and registers those as
|
|
``DataFetcher``s.
|
|
|
|
|
|
[[boot-repositories-querydsl]]
|
|
== Querydsl Repositories
|
|
|
|
Spring Data repositories that extend `QuerydslPredicateExecutor` or
|
|
`ReactiveQuerydslPredicateExecutor` and are annotated with `@GraphQlRepository` are
|
|
detected and considered as candidates for `DataFetcher`
|
|
<<index.adoc#data-querydsl-registration,auto registration>> for matching top-level queries.
|
|
|
|
|
|
|
|
[[boot-graphql-web]]
|
|
== Web Endpoints
|
|
|
|
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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
|
|
<<index.adoc#web-interception,Web Interception>> for GraphQL over HTTP and WebSocket
|
|
requests.
|
|
|
|
Declare a `ThreadLocalAccessor` bean to assist with the propagation of `ThreadLocal`
|
|
values of interest in <<index.adoc#execution-context-webmvc,Spring MVC>>.
|
|
|
|
|
|
|
|
[[boot-graphql-exception-handling]]
|
|
== Exception
|
|
|
|
Spring GraphQL 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. See
|
|
<<execution-exceptions>> for details.
|
|
|
|
The Boot starter detects beans of type `DataFetcherExceptionResolver` and registers them
|
|
automatically with the `GraphQlSource.Builder`.
|
|
|
|
|
|
|
|
[[boot-graphql-batch-loader-registry]]
|
|
== BatchLoaderRegistry
|
|
|
|
Spring GraphQL supports the GraphQL Java <<execution-batching,batch feature>> and provides
|
|
a `BatchLoaderRegistry` to store registrations of batch loading functions. The Boot
|
|
starter declares a `BatchLoaderRegistry` bean and configures the `ExecutionGraphQlService`
|
|
with it so that applications can simply autowire the registry into their controllers and
|
|
register batch loading functions.
|
|
|
|
For example:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
public class BookController {
|
|
|
|
public BookController(BatchLoaderRegistry registry) {
|
|
registry.forTypePair(Long.class, Author.class).registerBatchLoader((authorIds, env) -> {
|
|
// load authors
|
|
});
|
|
}
|
|
|
|
@SchemaMapping
|
|
public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
|
|
return loader.load(book.getAuthorId());
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
|
|
|
|
|
|
[[boot-graphql-graphiql]]
|
|
== GraphiQL
|
|
|
|
The Spring Boot starter includes a https://github.com/graphql/graphiql[GraphiQL] page
|
|
that is exposed at "/graphiql" by default. You can configure this as follows:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
spring.graphql.graphiql.enabled=true
|
|
spring.graphql.graphiql.path=/graphiql
|
|
----
|
|
|
|
|
|
|
|
[[boot-graphql-metrics]]
|
|
== 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:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,properties,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
management.endpoints.web.exposure.include=health,metrics,info
|
|
----
|
|
|
|
|
|
[[boot-graphql-metrics-request-timer]]
|
|
=== Request Timer
|
|
|
|
A Request metric timer is available at `/actuator/metrics/graphql.request`.
|
|
|
|
[cols="1,2,2"]
|
|
|===
|
|
|Tag | Description| Sample values
|
|
|
|
|outcome
|
|
|Request outcome
|
|
|"SUCCESS", "ERROR"
|
|
|===
|
|
|
|
|
|
[[boot-graphql-metrics-datafetcher-timer]]
|
|
=== `DataFetcher` Timer
|
|
|
|
A `DataFetcher` metric timer is available at `/actuator/metrics/graphql.datafetcher`.
|
|
|
|
[cols="1,2,2"]
|
|
|===
|
|
|Tag | Description| Sample values
|
|
|
|
|path
|
|
|data fetcher path
|
|
|"Query.project"
|
|
|
|
|outcome
|
|
|data fetching outcome
|
|
|"SUCCESS", "ERROR"
|
|
|===
|
|
|
|
|
|
[[boot-graphql-metrics-error-counter]]
|
|
=== Error Counter
|
|
|
|
A GraphQL error metric counter is available at `/actuator/metrics/graphql.error`.
|
|
|
|
[cols="1,2,2"]
|
|
|===
|
|
|Tag | Description| Sample values
|
|
|
|
|errorType
|
|
|error type
|
|
|"DataFetchingException"
|
|
|
|
|errorPath
|
|
|error JSON Path
|
|
|"$.project"
|
|
|===
|
|
|
|
|
|
|
|
[[boot-graphql-testing]]
|
|
== Testing
|
|
|
|
For Spring GraphQL testing support, add the below to your classpath and that will make
|
|
a `WebGraphQlTester` available for injection into tests:
|
|
|
|
[source,groovy,indent=0,subs="verbatim,quotes",role="primary"]
|
|
.Gradle
|
|
----
|
|
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
|
|
}
|
|
----
|
|
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
.Maven
|
|
----
|
|
<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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@SpringBootTest
|
|
@AutoConfigureMockMvc
|
|
@AutoConfigureGraphQlTester
|
|
public class MockMvcGraphQlTests {
|
|
|
|
@Autowired
|
|
private WebGraphQlTester graphQlTester;
|
|
|
|
}
|
|
----
|
|
|
|
For GraphQL over HTTP with Spring WebFlux, using a
|
|
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.with-mock-environment[mock server]:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@SpringBootTest
|
|
@AutoConfigureWebTestClient
|
|
@AutoConfigureGraphQlTester
|
|
public class MockMvcGraphQlTests {
|
|
|
|
@Autowired
|
|
private WebGraphQlTester graphQlTester;
|
|
|
|
}
|
|
----
|
|
|
|
For GraphQL over HTTP with a
|
|
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.with-running-server[running server]:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
@AutoConfigureGraphQlTester
|
|
public class MockMvcGraphQlTests {
|
|
|
|
@Autowired
|
|
private WebGraphQlTester graphQlTester;
|
|
|
|
}
|
|
----
|
|
|
|
Subscriptions can be tested without WebSocket as shown below:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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`.
|