Spring for GraphQL Documentation
repository·main·Indexed 23 days ago
https://github.com/spring-projects/spring-graphqlIntegration for GraphQL support in Spring applications, built on top of GraphQL Java. It provides an annotation-based programming model using @Controller, @SchemaMapping, @QueryMapping, @MutationMapping, and @SubscriptionMapping. The project includes a GraphQlClient interface with transport-specific implementations for HTTP (sync and non-blocking), WebSockets, and RSocket, as well as support for DGS Codegen for type-safe API generation.
What's inside Spring for GraphQL
- Spring for GraphQL provides GraphQL support for Spring applications by leveraging GraphQL Java. It allows developers to build GraphQL APIs within the Spring ecosystem.
Understand `GraphQlSource` and its configuration
mainGraphQlSourceis the contract used to expose the underlyinggraphql.GraphQLinstance and provides a builder API to configure it.In a Spring Boot application, the Boot Starter automatically initializes the
GraphQlSource.Builderto:- Load schema files from
classpath:graphql/**(defaulting tosrc/main/resources/graphql). - Detect
RuntimeWiringConfigurerbeans. - Detect
Instrumentationbeans for observability. - Detect
DataFetcherExceptionResolverandSubscriptionExceptionResolverbeans for exception handling.
To perform advanced customizations, you can declare a
GraphQlSourceBuilderCustomizerbean.@Configuration(proxyBeanMethods = false) public class GraphQlConfig { @Bean public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() { return (builder) -> builder.configureGraphQl((graphQlBuilder) -> graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider())); } }- Load schema files from
Understand Selection Sets vs Spring Data Projections
mainSpring for GraphQL is not a data gateway that translates GraphQL queries directly into SQL/JSON. Instead, it uses client-driven selection and server-side transformations complementarily.
- Selection Sets: By default, Querydsl and QBE integrations turn the GraphQL selection set into property path hints used by Spring Data to limit the data fetched.
- Projections: Used when you need to transform or reduce the underlying data model to match the GraphQL schema.
- Closed Interface Projections: Useful when you cannot partially materialize an aggregate but want to expose a subset of properties.
- Open Interface Projections: Use Spring's
@Valueand SpEL to apply lightweight transformations (concatenations, computations). - DTO Projections: Offer high customization via constructors or getters. Often used with Java records, but require all fields in the projection to be present in the database query result.
Auto-register Querydsl repositories with @GraphQlRepository
mainIf you annotate a repository with
@GraphQlRepository, Spring for GraphQL will automatically register it for queries that:- Do not already have a registered
DataFetcher. - Have a return type matching the repository's domain type.
Key behaviors:
- Naming: By default, the GraphQL type name must match the simple name of the repository domain type. Use the
typeNameattribute of@GraphQlRepositoryto override this. - Pagination: For paginated queries, the domain type name must match the
Connectiontype name without theConnectionsuffix (e.g.,BookmatchesBooksConnection). Auto-registered pagination is offset-based with 20 items per page. - Customization: If the repository implements
QuerydslBinderCustomizerorReactiveQuerydslBinderCustomizer, these customizations are automatically applied. - Boot Starter: The Spring Boot starter automatically detects these beans and initializes the
RuntimeWiringConfigurer.
- Do not already have a registered
Handle File Uploads in GraphQL
mainSpring for GraphQL does not support the
graphql-multipart-request-specdirectly.If you need to support file uploads via the multipart request specification, it is recommended to use the third-party library
multipart-spring-graphql.How GraphQlClient and its transport extensions work
mainSpring for GraphQL provides a
GraphQlClientinterface that defines a common workflow for GraphQL requests, making the API independent of the underlying transport. You choose a specific implementation based on your transport needs:HttpSyncGraphQlClient: UsesRestClientfor blocking (synchronous) HTTP requests.HttpGraphQlClient: UsesWebClientfor non-blocking (asynchronous) HTTP requests.WebSocketGraphQlClient: Executes requests over a shared, multiplexed WebSocket connection. It is connection-oriented and can be started explicitly via.start()or transparently on the first request.RSocketGraphQlClient: UsesRSocketRequesterfor RSocket requests. Like WebSockets, it is connection-oriented and multiplexed.
All these clients use a
Builderto configure transport-specific options, while inheriting common options from a baseBuilder(such asDocumentSourcestrategies and interceptors).Customize GraphQL Observation Metadata
mainIf you need to customize the metadata (KeyValues) produced by GraphQL observations, you can configure a custom convention.
If you are using Spring Boot, the preferred method is to contribute your custom convention as a bean. You can target specific instrumentation types:
org.springframework.graphql.observation.DefaultExecutionRequestObservationConvention(for Server Requests)org.springframework.graphql.observation.DefaultDataFetcherObservationConvention(for DataFetchers)org.springframework.graphql.observation.DefaultDataLoaderObservationConvention(for DataLoaders)
Apply fine-grained security to GraphQL data fetching
mainTo implement field-level or data-specific security, apply Spring Security annotations like
@PreAuthorizeor@Secureddirectly to the service methods used by your data fetchers.This works because Spring for GraphQL supports Context Propagation, which ensures that the Spring Security context is available at the data fetching level during request execution.
Handle Exceptions in Spring GraphQL
mainSpring for GraphQL provides several ways to handle exceptions:
DataFetcherExceptionResolver: Used to resolve exceptions occurring during data fetching into a list ofgraphql.GraphQLErrors. The Boot starter detects these beans automatically. You can extendDataFetcherExceptionResolverAdapterfor convenience.@GraphQlExceptionHandler: Part of the annotated controller model, allowing you to handle exceptions with specific method signatures.SubscriptionExceptionResolver: Specifically for resolving exceptions that occur within a subscriptionPublisher.- Global Errors: Errors occurring during parsing or validation (before execution) cannot be handled by
DataFetcherExceptionResolver. These must be handled via transport-level interceptors (e.g.,WebGraphQlInterceptor).
Errors are categorized using
ErrorType(e.g.,BAD_REQUEST,UNAUTHORIZED,FORBIDDEN,NOT_FOUND,INTERNAL_ERROR). Unresolved exceptions default toINTERNAL_ERRORwith an opaque message to avoid leaking implementation details.Choose between @BatchMapping and BatchLoaderRegistry
mainWhen loading related entities, you have two primary options:
@BatchMapping: A high-level shortcut that minimizes boilerplate. It is recommended for most straightforward use cases.BatchLoaderRegistry: A lower-level API that offers more flexibility for advanced use cases.
Why use
BatchLoaderRegistry? It is the preferred way to register data loaders because it:- Provides access to the same
GraphQLContextused by@BatchMappingmethods. - Ensures proper Context Propagation to the batch loading functions.
While you can register
DataLoaders manually, doing so bypasses these automatic benefits.Use Annotated Controllers for GraphQL Data Fetching
mainSpring for GraphQL uses an annotation-based programming model where
@Controllerbeans act as data fetching components. Methods within these controllers are mapped to GraphQL fields using annotations, which theAnnotatedControllerConfigurerthen registers asDataFetchers viaRuntimeWiring.Builder. If you are using the Spring Boot starter, this configuration is handled automatically.@Controller public class GreetingController { @QueryMapping public String hello() { return "Hello, world!"; } }Optimize Batch Loading strategies
mainBecause
DataLoaders cache loaded entities by their key for the entire lifetime of a request, you must balance memory consumption against the number of I/O calls. Two common strategies include:1. Filtering at the @SchemaMapping level
Load all related entities for a parent in the
DataLoader, then apply filters within the@SchemaMappingmethod.- Pros: Fewer I/O calls; high cache hit rate.
- Cons: Higher memory consumption (loads data that might be filtered out).
- Best for: Small groups of related entities or highly popular filter criteria.
2. Using Composed Keys
Include the filter criteria as part of the
DataLoaderkey (e.g., a key composed of both thePersonand theFilter).- Pros: Lower memory consumption (only loads what is needed).
- Cons: More I/O operations; potential for duplicate entities in the cache because the same entity might be loaded under different composed keys.
- Best for: Large datasets where filters are niche or highly specific.