Reinstate Spring for GraphQL auto-configuration

This commit adds the Spring for GraphQL auto-configuration back
into Spring Boot 3.0, now that a 1.1.0 release is scheduled with the
required baseline. This release also needs GraphQL Java 19.0 as a
baseline.

Closes gh-31809
This commit is contained in:
Brian Clozel
2022-07-20 13:57:17 +02:00
parent ab469e8b6a
commit 38f1bc9793
125 changed files with 7728 additions and 4 deletions

View File

@@ -888,6 +888,55 @@ NOTE: Only caches that are configured on startup are bound to the registry.
For caches not defined in the caches configuration, such as caches created on the fly or programmatically after the startup phase, an explicit registration is required.
A `CacheMetricsRegistrar` bean is made available to make that process easier.
[[actuator.metrics.supported.spring-graphql]]
==== Spring GraphQL Metrics
Auto-configuration enables the instrumentation of GraphQL queries, for any supported transport.
Spring Boot records a `graphql.request` timer with:
[cols="1,2,2"]
|===
|Tag | Description| Sample values
|outcome
|Request outcome
|"SUCCESS", "ERROR"
|===
A single GraphQL query can involve many `DataFetcher` calls, so there is a dedicated `graphql.datafetcher` timer:
[cols="1,2,2"]
|===
|Tag | Description| Sample values
|path
|data fetcher path
|"Query.project"
|outcome
|data fetching outcome
|"SUCCESS", "ERROR"
|===
The `graphql.request.datafetch.count` https://micrometer.io/docs/concepts#_distribution_summaries[distribution summary] counts the number of non-trivia
This metric is useful for detecting "N+1" data fetching issues and considering batch loading; it provides the `"TOTAL"` number of data fetcher calls ma
More options are available for <<application-properties#application-properties.actuator.management.metrics.distribution.maximum-expected-value, configu
A single response can contain many GraphQL errors, counted by the `graphql.error` counter:
[cols="1,2,2"]
|===
|Tag | Description| Sample values
|errorType
|error type
|"DataFetchingException"
|errorPath
|error JSON Path
|"$.project"
|===
[[actuator.metrics.supported.jdbc]]

View File

@@ -83,6 +83,9 @@
:spring-framework: https://spring.io/projects/spring-framework
:spring-framework-api: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/javadoc-api/org/springframework
:spring-framework-docs: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/reference/html
:spring-graphql: https://spring.io/projects/spring-graphql
:spring-graphql-api: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/api/
:spring-graphql-docs: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/reference/html/
:spring-integration: https://spring.io/projects/spring-integration
:spring-integration-docs: https://docs.spring.io/spring-integration/docs/{spring-integration-version}/reference/html/
:spring-kafka-docs: https://docs.spring.io/spring-kafka/docs/{spring-kafka-version}/reference/html/

View File

@@ -405,6 +405,42 @@ TIP: Sometimes writing Spring WebFlux tests is not enough; Spring Boot can help
[[features.testing.spring-boot-applications.spring-graphql-tests]]
==== Auto-configured Spring GraphQL Tests
Spring GraphQL offers a dedicated testing support module; you'll need to add it to your project:
.Maven
[source,xml,indent=0,subs="verbatim"]
----
<dependencies>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Unless already present in the compile scope -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
----
.Gradle
[source,gradle,indent=0,subs="verbatim"]
----
dependencies {
testImplementation("org.springframework.graphql:spring-graphql-test")
// Unless already present in the implementation configuration
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}
----
This testing module ships the {spring-graphql-docs}/#testing-graphqltester[GraphQlTester].
spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc
[[features.testing.spring-boot-applications.autoconfigured-spring-data-cassandra]]
==== Auto-configured Data Cassandra Tests
You can use `@DataCassandraTest` to test Cassandra applications.

View File

@@ -19,6 +19,8 @@ include::web/spring-security.adoc[]
include::web/spring-session.adoc[]
include::web/spring-graphql.adoc[]
include::web/spring-hateoas.adoc[]
include::web/whats-next.adoc[]

View File

@@ -0,0 +1,152 @@
[[web.graphql]]
== Spring for GraphQL
If you want to build GraphQL applications, you can take advantage of Spring Boot's auto-configuration for {spring-graphql}[Spring for GraphQL].
The Spring for GraphQL project is based on https://github.com/graphql-java/graphql-java[GraphQL Java].
You'll need the `spring-boot-starter-graphql` starter at a minimum.
Because GraphQL is transport-agnostic, you'll also need to have one or more additional starters in your application to expose your GraphQL API over the web:
[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
| `spring-boot-starter-rsocket`
| TCP, WebSocket
| Spring WebFlux on Reactor Netty
|===
[[web.graphql.schema]]
=== GraphQL Schema
A Spring GraphQL application requires a defined schema at startup.
By default, you can write ".graphqls" or ".gqls" schema files under `src/main/resources/graphql/**` and Spring Boot will pick them up automatically.
You can customize the locations with configprop:spring.graphql.schema.locations[] and the file extensions with configprop:spring.graphql.schema.file-extensions[].
In the following sections, we'll consider this sample GraphQL schema, defining two types and two queries:
[source,json,indent=0,subs="verbatim,quotes"]
----
include::{docs-resources}/graphql/schema.graphqls[]
----
NOTE: By default, https://spec.graphql.org/draft/#sec-Introspection[field introspection] will be allowed on the schema as it is required for tools such as GraphiQL.
If you wish to not expose information about the schema, you can disable introspection by setting configprop:spring.graphql.schema.introspection.enabled[] to `false`.
[[web.graphql.runtimewiring]]
=== GraphQL RuntimeWiring
The GraphQL Java `RuntimeWiring.Builder` can be used to register custom scalar types, directives, type resolvers, `DataFetcher`s, and more.
You can declare `RuntimeWiringConfigurer` beans in your Spring config to get access to the `RuntimeWiring.Builder`.
Spring Boot detects such beans and adds them to the {spring-graphql-docs}#execution-graphqlsource[GraphQlSource builder].
Typically, however, applications will not implement `DataFetcher` directly and will instead create {spring-graphql-docs}#controllers[annotated controllers].
Spring Boot will automatically detect `@Controller` classes with annotated handler methods and register those as `DataFetcher`s.
Here's a sample implementation for our greeting query with a `@Controller` class:
include::code:GreetingController[]
[[web.graphql.data-query]]
=== Querydsl and QueryByExample Repositories support
Spring Data offers support for both Querydsl and QueryByExample repositories.
Spring GraphQL can {spring-graphql-docs}#data[configure Querydsl and QueryByExample repositories as `DataFetcher`].
Spring Data repositories annotated with `@GraphQlRepository` and extending one of:
* `QuerydslPredicateExecutor`
* `ReactiveQuerydslPredicateExecutor`
* `QueryByExampleExecutor`
* `ReactiveQueryByExampleExecutor`
are detected by Spring Boot and considered as candidates for `DataFetcher` for matching top-level queries.
[[web.graphql.transports]]
=== Transports
[[web.graphql.transports.http-websocket]]
==== HTTP and WebSocket
The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default.
The path can be customized with configprop:spring.graphql.path[].
TIP: The HTTP endpoint for both Spring MVC and Spring WebFlux is provided by a `RouterFunction` bean with an `@Order` of `0`.
If you define your own `RouterFunction` beans, you may want to add appropriate `@Order` annotations to ensure that they are sorted correctly.
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, no additional dependency is required
* For both, the configprop:spring.graphql.websocket.path[] application property must be set
Spring GraphQL provides a {spring-graphql-docs}#web-interception[Web Interception] model.
This is quite useful for retrieving information from an HTTP request header and set it in the GraphQL context or fetching information from the same context and writing it to a response header.
With Spring Boot, you can declare a `WebInterceptor` bean to have it registered with the web transport.
{spring-framework-docs}/web.html#mvc-cors[Spring MVC] and {spring-framework-docs}/web-reactive.html#webflux-cors[Spring WebFlux] support CORS (Cross-Origin Resource Sharing) requests.
CORS is a critical part of the web config for GraphQL applications that are accessed from browsers using different domains.
Spring Boot supports many configuration properties under the `spring.graphql.cors.*` namespace; here's a short configuration sample:
[source,yaml,indent=0,subs="verbatim",configblocks]
----
spring:
graphql:
cors:
allowed-origins: "https://example.org"
allowed-methods: GET,POST
max-age: 1800s
----
[[web.graphql.transports.rsocket]]
==== RSocket
RSocket is also supported as a transport, on top of WebSocket or TCP.
Once the <<messaging#messaging.rsocket.server-auto-configuration,RSocket server is configured>>, we can configure our GraphQL handler on a particular route using configprop:spring.graphql.rsocket.mapping[].
For example, configuring that mapping as `"graphql"` means we can use that as a route when sending requests with the `RSocketGraphQlClient`.
Spring Boot auto-configures a `RSocketGraphQlClient.Builder<?>` bean that you can inject in your components:
include::code:RSocketGraphQlClientExample[tag=builder]
And then send a request:
include::code:RSocketGraphQlClientExample[tag=request]
[[web.graphql.exception-handling]]
=== Exceptions Handling
Spring GraphQL enables applications to register one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially.
The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}#execution-exceptions[Spring GraphQL exception handling documentation].
Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and register them with the `GraphQlSource.Builder`.
[[web.graphql.graphiql]]
=== GraphiQL and Schema printer
Spring GraphQL offers infrastructure for helping developers when consuming or developing a GraphQL API.
Spring GraphQL ships with a default https://github.com/graphql/graphiql[GraphiQL] page that is exposed at `"/graphiql"` by default.
This page is disabled by default and can be turned on with the configprop:spring.graphql.graphiql.enabled[] property.
Many applications exposing such a page will prefer a custom build.
A default implementation is very useful during development, this is why it is exposed automatically with <<using#using.devtools,`spring-boot-devtools`>> during development.
You can also choose to expose the GraphQL schema in text format at `/graphql/schema` when the configprop:spring.graphql.schema.printer.enabled[] property is enabled.