# Spring for GraphQL Documentation ## 1. Overview Spring for GraphQL provides support for Spring applications built on[GraphQL Java](https://www.graphql-java.com/). 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 for GraphQL is the successor of the[GraphQL Java Spring](https://github.com/graphql-java/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](https://github.com/spring-projects/spring-graphql/issues) to report a problem, discuss a design issue, or request a feature. To get started, check the Spring GraphQL starter on [start.spring.io](https://start.spring.io) and the[Samples](#samples) sections. ## 2. Requirements Spring for GraphQL requires the following as a baseline: * JDK8 * Spring Framework 5.3 * GraphQL Java 17 * Spring Data 2021.1.0 or later for QueryDSL or Query by Example ## 3. Web Transports Spring for GraphQL supports GraphQL requests over HTTP and over WebSocket. ### 3.1. HTTP `GraphQlHttpHandler` handles GraphQL over HTTP requests and delegates to the[Web Interception](#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](https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md)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 the[Web Endpoints](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.web-endpoints) section for details, or check `GraphQlWebMvcAutoConfiguration` or `GraphQlWebFluxAutoConfiguration`it contains, for the actual config. The Spring for GraphQL repository contains a Spring MVC[HTTP sample](https://github.com/spring-projects/spring-graphql/tree/main/samples/webmvc-http) application. ### 3.2. WebSocket `GraphQlWebSocketHandler` handles GraphQL over WebSocket requests based on the[protocol](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) defined in the[graphql-ws](https://github.com/enisdenjo/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](#web-interception) chain for further request execution. | |GraphQL Over WebSocket Protocols

There are two such protocols, one in the[subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws)library and another in the[graphql-ws](https://github.com/enisdenjo/graphql-ws) library. The former is not active and
succeeded by the latter. Read this[blog post](https://the-guild.dev/blog/graphql-over-websockets) 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](https://github.com/enisdenjo/graphql-ws#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 the[Web Endpoints](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.web-endpoints) section for details, or check `GraphQlWebMvcAutoConfiguration` or `GraphQlWebFluxAutoConfiguration`it contains, for the actual config. The Spring for GraphQL repository contains a WebFlux[WebSocket sample](https://github.com/spring-projects/spring-graphql/tree/main/samples/webflux-websocket) application. ### 3.3. Web Interception [HTTP](#web-http) and [WebSocket](#web-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 intercept(WebInput webInput, WebInterceptorChain chain) { webInput.configureExecutionInput((executionInput, builder) -> { Map map = ... ; return builder.extensions(map).build(); }); return chain.next(webInput); } } ``` Use `WebInterceptor` also to intercept responses, add HTTP response headers, or transform the `graphql.ExecutionResult`: ``` class MyInterceptor implements WebInterceptor { @Override public Mono intercept(WebInput webInput, WebInterceptorChain chain) { return chain.next(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 the[Web Endpoints](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.web-endpoints) section for details, or check `GraphQlWebMvcAutoConfiguration` or `GraphQlWebFluxAutoConfiguration`it contains, for the actual config. ## 4. Request Execution `GraphQlService` is the main Spring abstraction to call GraphQL Java to execute requests. Underlying transports, such as the [Web Transports](#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. ### 4.1. `GraphQLSource` `GraphQlSource` is a core Spring 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`](#execution-reactive-datafetcher), [Context Propagation](#execution-context), and[Exception Resolution](#execution-exceptions). The Spring Boot [starter](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql) initializes a`GraphQlSource` instance through the default `GraphQlSource.Builder` and also enables the following: * Load [schema files](#execution-graphqlsource-schema-resources) from a configurable location. * Expose [properties](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/application-properties.html#appendix.application-properties.web)that apply to `GraphQlSource.Builder`. * Detect [`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer) beans. * Detect [Instrumentation](https://www.graphql-java.com/documentation/instrumentation) beans for[GraphQL metrics](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/actuator.html#actuator.metrics.supported.spring-graphql). * Detect `DataFetcherExceptionResolver` beans for[exception resolution](#execution-exceptions). * Detect `GraphQlSourceBuilderCustomizer` beans for any other customizations. #### 4.1.1. Schema Resources `GraphQlSource.Builder` can be configured with one or more `Resource` instances to be parsed and merged together. That means schema files can be loaded from just about any location. By default, the Spring Boot starter[finds schema files](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.schema) from a well-known classpath location, but you can change that to a location on the file system via `FileSystemResource`, to byte content via `ByteArrayResource`, or implement a custom`Resource` that loads schema files from a remote location or storage. #### 4.1.2. Schema Creation By default, `GraphQlSource.Builder` uses the GraphQL Java `GraphQLSchemaGenerator` to create the `graphql.schema.GraphQLSchema`. This works for most applications, but if necessary, you can hook into the schema creation through the builder: ``` // Typically, accessed through Spring Boot's GraphQlSourceBuilderCustomizer GraphQlSource.Builder builder = ... builder.schemaResources(..) .configureRuntimeWiring(..) .schemaFactory((typeDefinitionRegistry, runtimeWiring) -> { // create GraphQLSchema }) ``` The primary reason for this is to create the schema through a federation library. #### 4.1.3. `RuntimeWiringConfigurer` You can use `RuntimeWiringConfigurer` to register: * Custom scalar types. * Directives handling code. * `TypeResolver`, if you need to override the[Default `TypeResolver`](#execution-graphqlsource-default-type-resolver) for a type. * `DataFetcher` for a field, although most applications will simply configure`AnnotatedControllerConfigurer`, which detects annotated, `DataFetcher` handler methods. The Spring Boot starter adds the `AnnotatedControllerConfigurer` by default. The Spring Boot starter detects beans of type `RuntimeWiringConfigurer` and registers them in the `GraphQlSource.Builder`. That means in most cases, you’ll' have something like the following in your configuration: ``` @Configuration public class GraphQlConfig { @Bean public RuntimeWiringConfigurer runtimeWiringConfigurer(BookRepository repository) { GraphQLScalarType scalarType = ... ; SchemaDirectiveWiring directiveWiring = ... ; DataFetcher dataFetcher = QuerydslDataFetcher.builder(repository).single(); return wiringBuilder -> wiringBuilder .scalar(scalarType) .directiveWiring(directiveWiring) .type("Query", builder -> builder.dataFetcher("book", dataFetcher)); } } ``` If you need to add a `WiringFactory`, e.g. to make registrations that take into account schema definitions, implement the alternative `configure` method that accepts both the`RuntimeWiring.Builder` and an output `List`. This allows you to add any number of factories that are then invoked in sequence. #### 4.1.4. Default `TypeResolver` `GraphQlSource.Builder` registers `ClassNameTypeResolver` as the default `TypeResolver`to use for GraphQL Interfaces and Unions that don’t already have such a registration through a [`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). The purpose of a `TypeResolver` in GraphQL Java is to determine the GraphQL Object type for values returned from the `DataFetcher` for a GraphQL Interface or Union field. `ClassNameTypeResolver` tries to match the simple class name of the value to a GraphQL Object Type and if it is not successful, it also navigates its super types including base classes and interfaces, looking for a match. `ClassNameTypeResolver` provides an option to configure a name extracting function along with `Class` to GraphQL Object type name mappings that should help to cover more corner cases. #### 4.1.5. Operation Caching GraphQL Java must *parse* and *validate* an operation before executing it. This may impact performance significantly. To avoid the need to re-parse and validate, an application may configure a `PreparsedDocumentProvider` that caches and reuses Document instances. The[GraphQL Java docs](https://www.graphql-java.com/documentation/execution/#query-caching) provide more details on query caching through a `PreparsedDocumentProvider`. In Spring GraphQL you can register a `PreparsedDocumentProvider` through`GraphQlSource.Builder#configureGraphQl`: . ``` // Typically, accessed through Spring Boot's GraphQlSourceBuilderCustomizer GraphQlSource.Builder builder = ... // Create provider PreparsedDocumentProvider provider = ... builder.schemaResources(..) .configureRuntimeWiring(..) .configureGraphQl(graphQLBuilder -> graphQLBuilder.preparsedDocumentProvider(provider)) ``` #### 4.1.6. Directives The GraphQL language supports directives that "describe alternate runtime execution and type validation behavior in a GraphQL document". Directives are similar to annotations in Java but declared on types, fields, fragments and operations in a GraphQL document. GraphQL Java provides the `SchemaDirectiveWiring` contract to help applications detect and handle directives. For more details, see[Schema Directives](https://www.graphql-java.com/documentation/sdl-directives/) in the GraphQL Java documentation. In Spring GraphQL you can register a `SchemaDirectiveWiring` through a[`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). The Spring Boot starter detects such beans, so you might have something like: ``` @Configuration public class GraphQlConfig { @Bean public RuntimeWiringConfigurer runtimeWiringConfigurer() { return builder -> builder.directiveWiring(new MySchemaDirectiveWiring()); } } ``` | |For an example of directives support check out the[Extended Validation for Graphql Java](https://github.com/graphql-java/graphql-java-extended-validation)library.| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### 4.2. 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](#execution-context-webflux). ### 4.3. Context Propagation Spring for GraphQL provides support to transparently propagate context from the [Web Transports](#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. #### 4.3.1. 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`](#web-interception) or `DataFetcher` switches to a different thread. Spring for 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 container) { container.put(KEY, RequestContextHolder.getRequestAttributes()); } @Override public void restoreValues(Map values) { if (values.containsKey(KEY)) { RequestContextHolder.setRequestAttributes((RequestAttributes) values.get(KEY)); } } @Override public void resetValues(Map values) { RequestContextHolder.resetRequestAttributes(); } } ``` A `ThreadLocalAccessor` can be registered in the [WebGraphHandler](#web-interception)builder. The Boot starter detects beans of this type and automatically registers them for Spring MVC application, see the[Web Endpoints](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.web-endpoints) section. #### 4.3.2. WebFlux A [Reactive `DataFetcher`](#execution-reactive-datafetcher) can rely on access to Reactor context that originates from the WebFlux request handling chain. This includes Reactor context added by [WebInterceptor](#web-interception) components. ### 4.4. 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 for GraphQL has a built-in `DataFetcherExceptionHandler` that is configured for use by the [`GraphQLSource`](#execution-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 for 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`. ### 4.5. Batch Loading Given a `Book` and its `Author`, we can create one `DataFetcher` for a book and another for its author. This allows selecting books with or without authors, but it means books and authors aren’t loaded together, which is especially inefficient when querying multiple books as the author for each book is loaded individually. This is known as the N+1 select problem. #### 4.5.1. `DataLoader` GraphQL Java provides a `DataLoader` mechanism for batch loading of related entities. You can find the full details in the[GraphQL Java docs](https://www.graphql-java.com/documentation/batching/). Below is a summary of how it works: 1. Register `DataLoader`'s in the `DataLoaderRegistry` that can load entities, given unique keys. 2. `DataFetcher`'s can access `DataLoader`'s and use them to load entities by id. 3. A `DataLoader` defers loading by returning a future so it can be done in a batch. 4. `DataLoader`'s maintain a per request cache of loaded entities that can further improve efficiency. #### 4.5.2. `BatchLoaderRegistry` The complete batching loading mechanism in GraphQL Java requires implementing one of several `BatchLoader` interface, then wrapping and registering those as `DataLoader`s with a name in the `DataLoaderRegistry`. The API in Spring GraphQL is slightly different. For registration, there is only one, central `BatchLoaderRegistry` exposing factory methods and a builder to create and register any number of batch loading functions: ``` @Configuration public class MyConfig { public MyConfig(BatchLoaderRegistry registry) { registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> { // return Mono }); // more registrations ... } } ``` The Spring Boot starter declares a `BatchLoaderRegistry` bean that you can inject into your configuration, as shown above, or into any component such as a controller in order register batch loading functions. In turn the `BatchLoaderRegistry` is injected into`ExecutionGraphQlService` where it ensures `DataLoader` registrations per request. By default, the `DataLoader` name is based on the class name of the target entity. This allows an `@SchemaMapping` method to declare a[DataLoader argument](#controllers-schema-mapping-data-loader) with a generic type, and without the need for specifying a name. The name, however, can be customized through the`BatchLoaderRegistry` builder, if necessary, along with other `DataLoader` options. For many cases, when loading related entities, you can use[@BatchMapping](#controllers-batch-mapping) controller methods, which are a shortcut for and replace the need to use `BatchLoaderRegistry` and `DataLoader` directly. s`BatchLoaderRegistry` provides other important benefits too. It supports access to the same `GraphQLContext` from batch loading functions and from `@BatchMapping` methods, as well as ensures [Context Propagation](#execution-context) to them. This is why applications are expected to use it. It is possible to perform your own `DataLoader` registrations directly but such registrations would forgo the above benefits. #### 4.5.3. Testing Batch Loading Start by having `BatchLoaderRegistry` perform registrations on a `DataLoaderRegistry`: ``` BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry(); // perform registrations... DataLoaderRegistry dataLoaderRegistry = DataLoaderRegistry.newRegistry().build(); batchLoaderRegistry.registerDataLoaders(dataLoaderRegistry, graphQLContext); ``` Now you can access and test individual `DataLoader`'s as follows: ``` DataLoader loader = dataLoaderRegistry.getDataLoader(Book.class.getName()); loader.load(1L); loader.loadMany(Arrays.asList(2L, 3L)); List books = loader.dispatchAndJoin(); // actual loading assertThat(books).hasSize(3); assertThat(books.get(0).getName()).isEqualTo("..."); // ... ``` ## 5. Data Integration Spring for GraphQL lets you leverage existing Spring technology, following common programming models to expose underlying data sources through GraphQL. This section discusses an integration layer for Spring Data that provides an easy way to adapt a Querydsl or a Query by Example repository to a `DataFetcher`, including the option for automated detection and GraphQL Query registration for repositories marked with `@GraphQlRepository`. ### 5.1. Querydsl Spring for GraphQL supports use of [Querydsl](http://www.querydsl.com/) to fetch data through the Spring Data[Querydsl extension](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#core.extensions). 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, QuerydslPredicateExecutor { } ``` Then use it to create a `DataFetcher`: ``` // For single result queries DataFetcher dataFetcher = QuerydslDataFetcher.builder(repository).single(); // For multi-result queries DataFetcher> dataFetcher = QuerydslDataFetcher.builder(repository).many(); ``` You can now register the above `DataFetcher` through a[`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). 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>` or `DataFetcher>`. Spring Data supports this variant for MongoDB. #### 5.1.1. Build Setup To configure Querydsl in your build, follow the[official reference documentation](https://querydsl.com/static/querydsl/latest/reference/html/ch02.html): For example: Gradle ``` dependencies { //... annotationProcessor "com.querydsl:querydsl-apt:$querydslVersion:jpa", 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final', 'javax.annotation:javax.annotation-api:1.3.2' } compileJava { options.annotationProcessorPath = configurations.annotationProcessor } ``` Maven ``` com.querydsl querydsl-apt ${querydsl.version} jpa provided org.hibernate.javax.persistence hibernate-jpa-2.1-api 1.0.2.Final javax.annotation javax.annotation-api 1.3.2 com.mysema.maven apt-maven-plugin ${apt-maven-plugin.version} process target/generated-sources/java com.querydsl.apt.jpa.JPAAnnotationProcessor ``` The [webmvc-http](https://github.com/spring-projects/spring-graphql/tree/main/samples/webmvc-http) sample uses Querydsl for`artifactRepositories`. #### 5.1.2. Customizations `QuerydslDataFetcher` supports customizing how GraphQL arguments are bound onto properties to create a Querydsl `Predicate`. By default, arguments are bound as "is equal to" for each available property. To customize that, you can use `QuerydslDataFetcher` builder methods to provide a `QuerydslBinderCustomizer`. A repository may itself be an instance of `QuerydslBinderCustomizer`. This is auto-detected and transparently applied during [Auto-Registration](#data-querydsl-registration). However, when manually building a `QuerydslDataFetcher` you will need to use builder methods to apply it. `QuerydslDataFetcher` supports interface and DTO projections to transform query results before returning these for further GraphQL processing. | |To learn what projections are, please refer to the[Spring Data docs](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections).
To understand how to use projections in GraphQL, please see [Selection Set vs Projections](#data-projections).| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| To use Spring Data projections with Querydsl repositories, create either a projection interface or a target DTO class and configure it through the `projectAs` method to obtain a`DataFetcher` producing the target type: ``` class Account { String name, identifier, description; Person owner; } interface AccountProjection { String getName(); String getIdentifier(); } // For single result queries DataFetcher dataFetcher = QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).single(); // For multi-result queries DataFetcher> dataFetcher = QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).many(); ``` #### 5.1.3. Auto-Registration If a repository is annotated with `@GraphQlRepository`, it is automatically registered for queries that do not already have a registered `DataFetcher` and whose return type matches that of the repository domain type. This includes both single value and multi-value queries. By default, the name of the GraphQL type returned by the query must match the simple name of the repository domain type. If needed, you can use the `typeName` attribute of`@GraphQlRepository` to specify the target GraphQL type name. Auto-registration detects if a given repository implements `QuerydslBinderCustomizer` and transparently applies that through `QuerydslDataFetcher` builder methods. Auto-registration is performed through a built-in `RuntimeWiringConfigurer` that can be obtained from `QuerydslDataFetcher`. The[Boot starter](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.data-query) automatically detects `@GraphQlRepository` beans and uses them to initialize the`RuntimeWiringConfigurer` with. Auto-registration does not support [customizations](#data-querybyexample-customizations). If you need that, you’ll need to use `QueryByExampleDataFetcher` to build and register the `DataFetcher` manually through a[`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). ### 5.2. Query by Example Spring Data supports the use of[Query by Example](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#query-by-example)to fetch data. Query by Example (QBE) is a simple querying technique that does not require you to write queries through store-specific query languages. Start by declaring a repository that is `QueryByExampleExecutor`: ``` public interface AccountRepository extends Repository, QueryByExampleExecutor { } ``` Use `QueryByExampleDataFetcher` to turn the repository into a `DataFecher`: ``` // For single result queries DataFetcher dataFetcher = QueryByExampleDataFetcher.builder(repository).single(); // For multi-result queries DataFetcher> dataFetcher = QueryByExampleDataFetcher.builder(repository).many(); ``` You can now register the above `DataFetcher` through a[`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). The `DataFetcher` uses the GraphQL arguments map to create the domain type of the repository and use that as the example object to fetch data with. Spring Data supports`QueryByExampleDataFetcher` for JPA, MongoDB, Neo4j, and Redis. If the repository is `ReactiveQueryByExampleExecutor`, the builder returns`DataFetcher>` or `DataFetcher>`. Spring Data supports this variant for MongoDB, Neo4j, Redis, and R2dbc. #### 5.2.1. Build Setup Query by Example is already included in the Spring Data modules for the data stores where it is supported, so no extra setup is required to enable it. #### 5.2.2. Customizations `QueryByExampleDataFetcher` supports interface and DTO projections to transform query results before returning these for further GraphQL processing. | |To learn what projections are, please refer to the[Spring Data documentation](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections).
To understand the role of projections in GraphQL, please see [Selection Set vs Projections](#data-projections).| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| To use Spring Data projections with Query by Example repositories, create either a projection interface or a target DTO class and configure it through the `projectAs` method to obtain a`DataFetcher` producing the target type: ``` class Account { String name, identifier, description; Person owner; } interface AccountProjection { String getName(); String getIdentifier(); } // For single result queries DataFetcher dataFetcher = QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).single(); // For multi-result queries DataFetcher> dataFetcher = QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).many(); ``` #### 5.2.3. Auto-Registration If a repository is annotated with `@GraphQlRepository`, it is automatically registered for queries that do not already have a registered `DataFetcher` and whose return type matches that of the repository domain type. This includes both single value and multi-value queries. By default, the name of the GraphQL type returned by the query must match the simple name of the repository domain type. If needed, you can use the `typeName` attribute of`@GraphQlRepository` to specify the target GraphQL type name. Auto-registration is performed through a built-in `RuntimeWiringConfigurer` that can be obtained from `QueryByExampleDataFetcher`. The[Boot starter](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.data-query) automatically detects `@GraphQlRepository` beans and uses them to initialize the`RuntimeWiringConfigurer` with. Auto-registration does not support [customizations](#data-querybyexample-customizations). If you need that, you’ll need to use `QueryByExampleDataFetcher` to build and register the `DataFetcher` manually through a[`RuntimeWiringConfigurer`](#execution-graphqlsource-runtimewiring-configurer). ### 5.3. Selection Set vs Projections A common question that arises is, how GraphQL selection sets compare to[Spring Data projections](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections)and what role does each play? The short answer is that Spring for GraphQL is not a data gateway that translates GraphQL queries directly into SQL or JSON queries. Instead, it lets you leverage existing Spring technology and does not assume a one for one mapping between the GraphQL schema and the underlying data model. That is why client-driven selection and server-side transformation of the data model can play complementary roles. To better understand, consider that Spring Data promotes domain-driven (DDD) design as the recommended approach to manage complexity in the data layer. In DDD, it is important to adhere to the constraints of an aggregate. By definition an aggregate is valid only if loaded in its entirety, since a partially loaded aggregate may impose limitations on aggregate functionality. In Spring Data you can choose whether you want your aggregate be exposed as is, or whether to apply transformations to the data model before returning it as a GraphQL result. Sometimes it’s enough to do the former, and by default the[Querydsl](#data-querydsl) and the [Query by Example](#data-querybyexample) integrations turn the GraphQL selection set into property path hints that the underlying Spring Data module uses to limit the selection. In other cases, it’s useful to reduce or even transform the underlying data model in order to adapt to the GraphQL schema. Spring Data supports this through Interface and DTO Projections. Interface projections define a fixed set of properties to expose where properties may or may not be `null`, depending on the data store query result. There are two kinds of interface projections both of which determine what properties to load from the underlying data source: * [Closed interface projections](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces.closed)are helpful if you cannot partially materialize the aggregate object, but you still want to expose a subset of properties. * [Open interface projections](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces.open)leverage Spring’s `@Value` annotation and[SpEL](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions) expressions to apply lightweight data transformations, such as concatenations, computations, or applying static functions to a property. DTO projections offer a higher level of customization as you can place transformation code either in the constructor or in getter methods. DTO projections materialize from a query where the individual properties are determined by the projection itself. DTO projections are commonly used with full-args constructors (e.g. Java records), and therefore they can only be constructed if all required fields (or columns) are part of the database query result. ## 6. Annotated Controllers Spring for 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 for GraphQL uses `RuntimeWiring.Builder` to register the above handler method as a`graphql.schema.DataFetcher` for the query named "hello". ### 6.1. 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. `AnnotatedControllerConfigurer` 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 `AnnotatedControllerConfigurer` as a bean and adds all `RuntimeWiringConfigurer` beans to `GraphQlSource.Builder` and that enables support for annotated `DataFetcher`s, see the[GraphQL RuntimeWiring](https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/html/web.html#web.graphql.runtimewiring) section in the Boot starter documentation. ### 6.2. `@SchemaMapping` 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 newPublications() { // ... } } ``` `@SchemaMapping` handler methods have flexible signatures and can choose from a range of method arguments and return values.. #### 6.2.1. Method Signature Schema mapping handler methods can have any of the following method arguments: | Method Argument | Description | |-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `@Argument` | For access to a named field argument converted to a higher-level, typed Object.
See [`@Argument`](#controllers-schema-mapping-argument). | | `@Arguments` | For access to all field arguments converted to a higher-level, typed Object.
See [`@Arguments`](#controllers-schema-mapping-arguments). | | `@ProjectedPayload` Interface |For access to field arguments through a project interface.
See [`@ProjectPayload` Interface](#controllers-schema-mapping-projectedpayload-argument).| | Source | For access to the source (i.e. parent/container) instance of the field.
See [Source](#controllers-schema-mapping-source). | | `DataLoader` | For access to a `DataLoader` in the `DataLoaderRegistry`.
See [`DataLoader`](#controllers-schema-mapping-data-loader). | | `@ContextValue` | For access to a value from the localContext, if it is an instance of `GraphQLContext`,
or from the `GraphQLContext` of `DataFetchingEnvironment`. | | `GraphQLContext` | For access to the context from the `DataFetchingEnvironment`. | | `java.security.Principal` | Obtained from the Spring Security context, if available. | | `@AuthenticationPrincipal` | For access to `Authentication#getPrincipal()` from the Spring Security context. | |`DataFetchingFieldSelectionSet`| For access to the selection set for the query through the `DataFetchingEnvironment`. | | `Locale`, `Optional` | For access to the `Locale` from the `DataFetchingEnvironment`. | | `DataFetchingEnvironment` | For direct access to the underlying `DataFetchingEnvironment`. | Schema mapping handler methods can return any value, including Reactor `Mono` and`Flux` as described in [Reactive `DataFetcher`](#execution-reactive-datafetcher). #### 6.2.2. `@Argument` In GraphQL Java, `DataFetchingEnvironment` provides access to a map of field-specific argument values. The values can be simple scalar values (e.g. String, Long), a `Map` of values for more complex input, or a `List` of values. Use the `@Argument` annotation to inject a named field argument into a handler method. The method parameter can be a higher-level, typed Object of any type. It is created and initialized from the named field argument value(s), either matching them to single data constructor parameters, or using the default constructor and then matching keys onto Object properties through a `org.springframework.validation.DataBinder`: ``` @Controller public class BookController { @QueryMapping public Book bookById(@Argument Long id) { // ... } @MutationMapping public Book addBook(@Argument BookInput bookInput) { // ... } } ``` By default, if the method parameter name is available (requires the `-parameters` compiler flag with Java 8+ or debugging info from the compiler), it is used to look up the argument. If needed, you can customize the name through the annotation, e.g. `@Argument("bookInput")`. | |The `@Argument` annotation does not have a "required" flag, nor the option to
specify a default value. Both of these can be specified at the GraphQL schema level and
are enforced by the GraphQL Engine.| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| You can use `@Argument` on a `Map` argument, to obtain all argument values. The name attribute on `@Argument` must not be set. #### 6.2.3. `@Arguments` Use the `@Arguments` annotation, if you want to bind the full arguments map onto a single target Object, in contrast to `@Argument`, which binds a specific, named argument. For example, `@Argument BookInput bookInput` uses the value of the argument "bookInput" to initialize `BookInput`, while `@Arguments` uses the full arguments map and in that case, top-level arguments are bound to `BookInput` properties. #### 6.2.4. `@Argument(s)` Validation If a [Bean Validation](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation-beanvalidation-overview)`Validator` (or typically, a `LocalValidatorFactoryBean`) bean is present in the application context, the `AnnotatedControllerConfigurer` will auto-detect it and configure support for validation. Controller arguments annotated with `@Valid` and `@Validated` are then validated before method invocation. Bean Validation lets you declare constraints on types, as the following example shows: ``` public class BookInput { @NotNull private String title; @NotNull @Size(max=13) private String isbn; } ``` We can then mark our argument for validation with `@Valid`: ``` @Controller public class BookController { @MutationMapping public Book addBook(@Argument @Valid BookInput bookInput) { // ... } } ``` If an error occurs during validation, a `ConstraintViolationException` is thrown and can be later [resolved with a custom `DataFetcherExceptionResolver`](#execution-exceptions). | |Unlike Spring MVC, handler method signatures do not support the injection of `BindingResult`for reacting to validation errors: those are globally dealt with as exceptions.| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| #### 6.2.5. `@ProjectPayload` Interface As an alternative to using complete Objects with [`@Argument`](#controllers-schema-mapping-argument), you can also use a projection interface to access GraphQL request arguments through a well-defined, minimal interface. Argument projections are provided by[Spring Data’s Interface projections](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces)when Spring Data is on the class path. To make use of this, create an interface annotated with `@ProjectedPayload` and declare it as a controller method parameter. If the parameter is annotated with `@Argument`, it applies to an individual argument within the `DataFetchingEnvironment.getArguments()`map. When declared without `@Argument`, the projection works on top-level arguments in the complete arguments map. For example: ``` @Controller public class BookController { @QueryMapping public Book bookById(BookIdProjection bookId) { // ... } @MutationMapping public Book addBook(@Argument BookInputProjection bookInput) { // ... } } @ProjectedPayload interface BookIdProjection { Long getId(); } @ProjectedPayload interface BookInputProjection { String getName(); @Value("#{target.author + ' ' + target.name}") String getAuthorAndName(); } ``` #### 6.2.6. 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. | |A [`@BatchMapping`](#controllers-batch-mapping) handler method can batch load all authors for a query,
given a list of source/parent books objects.| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------| #### 6.2.7. `DataLoader` When you register a batch loading function for an entity, as explained in[Batch Loading](#execution-batching), you can access the `DataLoader` for the entity by declaring a method argument of type `DataLoader` and use it to load the entity: ``` @Controller public class BookController { public BookController(BatchLoaderRegistry registry) { registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> { // return Map }); } @SchemaMapping public CompletableFuture author(Book book, DataLoader loader) { return loader.load(book.getAuthorId()); } } ``` By default, `BatchLoaderRegistry` uses the full class name of the value type (e.g. the class name for `Author`) for the key of the registration, and therefore simply declaring the `DataLoader` method argument with generic types provides enough information to locate it in the `DataLoaderRegistry`. As a fallback, the `DataLoader` method argument resolver will also try the method argument name as the key but typically that should not be necessary. Note that for many cases with loading related entities, where the `@SchemaMapping` simply delegates to a `DataLoader`, you can reduce boilerplate by using a[@BatchMapping](#controllers-batch-mapping) method as described in the next section. ### 6.3. `@BatchMapping` [Batch Loading](#execution-batching) addresses the N+1 select problem through the use of an`org.dataloader.DataLoader` to defer the loading of individual entity instances, so they can be loaded together. For example: ``` @Controller public class BookController { public BookController(BatchLoaderRegistry registry) { registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> { // return Map }); } @SchemaMapping public CompletableFuture author(Book book, DataLoader loader) { return loader.load(book.getAuthorId()); } } ``` For the straight-forward case of loading an associated entity, shown above, the`@SchemaMapping` method does nothing more than delegate to the `DataLoader`. This is boilerplate that can be avoided with a `@BatchMapping` method. For example: ``` @Controller public class BookController { @BatchMapping public Mono> author(List books) { // ... } } ``` The above becomes a batch loading function in the `BatchLoaderRegistry`where keys are `Book` instances and the loaded values their authors. In addition, a`DataFetcher` is also transparently bound to the `author` field of the type `Book`, which simply delegates to the `DataLoader` for authors, given its source/parent `Book` instance. | |To be used as a unique key, `Book` must implement `hashcode` and `equals`.| |---|--------------------------------------------------------------------------| By default, the field name defaults to the method name, while the type name defaults to the simple class name of the input `List` element type. Both can be customized through annotation attributes. The type name can also be inherited from a class level`@SchemaMapping`. #### 6.3.1. Method Signature Batch mapping methods support the following arguments: | Method Argument | Description | |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| | `List` | The source/parent objects. | |`java.security.Principal`| Obtained from Spring Security context, if available. | | `@ContextValue` |For access to a value from the `GraphQLContext` of `BatchLoaderEnvironment`,
which is the same context as the one from the `DataFetchingEnvironment`.| | `GraphQLContext` | For access to the context from the `BatchLoaderEnvironment`,
which is the same context as the one from the `DataFetchingEnvironment`. | |`BatchLoaderEnvironment` | The environment that is available in GraphQL Java to a`org.dataloader.BatchLoaderWithContext`. | Batch mapping methods can return: | Return Type | Description | |---------------------|--------------------------------------------------------------------------------------------------------------------------| | `Mono>` | A map with parent objects as keys, and batch loaded objects as values. | | `Flux` |A sequence of batch loaded objects that must be in the same order as the source/parent
objects passed into the method.| |`Map`, `List`| Imperative variants, e.g. without remote calls to make. | ## 7. Security The path to a [Web](#web-transports) 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](#execution-context) that aims to make Security, and other context, available at the data fetching level. The Spring for GraphQL repository contains samples for[Spring MVC](https://github.com/spring-projects/spring-graphql/tree/main/samples/webmvc-http-security) and for[WebFlux](https://github.com/spring-projects/spring-graphql/tree/main/samples/webflux-security). ## 8. Testing It’s possible to test GraphQL requests with Spring’s `WebTestClient`, just sending and receiving JSON, but a number of GraphQL specific details make this approach more cumbersome than is necessary. To get the full testing support, you’ll need to add the `spring-graphql-test` dependdency in your build: Gradle ``` dependencies { // ... testImplementation 'org.springframework.graphql:spring-graphql-test:1.0.0-SNAPSHOT' } ``` Maven ``` org.springframework.graphql spring-graphql-test 1.0.0-SNAPSHOT test ``` ### 8.1. `GraphQlTester` `GraphQlTester` defines a workflow to test GraphQL requests with the following benefits: * 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(); ``` ### 8.2. `WebGraphQlTester` `WebGraphQlTester` extends `GraphQlTester` to add a workflow and configuration specific to [Web Transports](#web-transports), and it always verifies GraphQL HTTP responses are 200 (OK). To create `WebGraphQlTester`, you need one of the following inputs: * `WebTestClient` — perform requests as an HTTP client, either against [HTTP](#web-http)handlers without a server, or against a live server. * `WebGraphQlHandler` — perform requests through the [Web Interception](#web-interception) chain used by both [HTTP](#web-http) and [WebSocket](#web-websocket) handlers, which in effect is testing without a Web framework. One reason to use this is for [Subscriptions](#testing-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, the same but using `MockMvcWebTestClient`: ``` WebApplicationContext context = ... ; WebTestClient client = MockMvcWebTestClient.bindToApplicationContext(context) .configureClient() .baseUrl("/graphql") .build(); WebGraphQlTester tester = WebGraphQlTester.builder(client).build(); ``` To test against a live, running server: ``` WebTestClient client = WebTestClient.bindToServer() .baseUrl("http://localhost:8080/graphql") .build(); WebGraphQlTester tester = WebGraphQlTester.builder(client).build(); ``` `WebGraphQlTester` supports setting HTTP request headers and access to HTTP response headers. This may be useful to inspect or set security related headers. ``` this.graphQlTester.queryName("{ myQuery }") .httpHeaders(headers -> headers.setBasicAuth("rob", "...")) .execute() .httpHeadersSatisfy(headers -> { // check response headers }) .path("myQuery.field1").entity(String.class).isEqualTo("value1") .path("myQuery.field2").entity(String.class).isEqualTo("value2"); ``` You can also set default request headers at the builder level: ``` WebGraphQlTester tester = WebGraphQlTester.builder(client) .defaultHttpHeaders(headers -> headers.setBasicAuth("rob", "...")) .build(); ``` ### 8.3. Queries Below is an example query test using[JsonPath](https://github.com/json-path/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. You can also create query files with extensions `.graphql` or `.gql` under `"graphql/"` on the classpath and refer to them by file name. For example, given a file called`projectReleases.graphql` in `src/main/resources/graphql`, with content: ``` query projectReleases($slug: ID!) { project(slug: $slug) { releases { version } } } ``` You can write the same test as follows: ``` graphQlTester.queryName("projectReleases") (1) .variable("slug", "spring-framework") (2) .execute() .path("project.releases[*].version") .entityList(String.class) .hasSizeGreaterThan(1); ``` |**1**|Refer to the query in the file named "projectReleases".| |-----|-------------------------------------------------------| |**2**| Set the `slug` variable. | | |The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion.| |---|---------------------------------------------------------------------------------------| ### 8.4. Errors Verify won’t succeed when there are errors under the "errors" key in the response. 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 to expect an error, and in contrast to `filter`, throw an assertion error when it doesn’t exist in the response: ``` graphQlTester.query(query) .execute() .errors() .expect(error -> ...) .verify() .path("project.releases[*].version") .entityList(String.class) .hasSizeGreaterThan(1); ``` 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(); ``` ### 8.5. 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 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 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](#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 for GraphQL does not support testing with a WebSocket client, and it cannot be used for integration test of GraphQL over WebSocket requests. ## 9. Samples This Spring for GraphQL repository contains [sample applications](https://github.com/spring-projects/spring-graphql/tree/main/samples) 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 ```