Spring for GraphQL Documentation

repository·main·Indexed 23 days ago

https://github.com/spring-projects/spring-graphql

Integration 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.

Tokens
15.6K
Snippets
21
Records
78
Agent score
82%

What's inside Spring for GraphQL

  1. Understand `GraphQlSource` and its configuration

    main

    GraphQlSource is the contract used to expose the underlying graphql.GraphQL instance and provides a builder API to configure it.

    In a Spring Boot application, the Boot Starter automatically initializes the GraphQlSource.Builder to:

    • Load schema files from classpath:graphql/** (defaulting to src/main/resources/graphql).
    • Detect RuntimeWiringConfigurer beans.
    • Detect Instrumentation beans for observability.
    • Detect DataFetcherExceptionResolver and SubscriptionExceptionResolver beans for exception handling.

    To perform advanced customizations, you can declare a GraphQlSourceBuilderCustomizer bean.

    @Configuration(proxyBeanMethods = false)
    public class GraphQlConfig {
    
    @Bean
    	public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() {
    		return (builder) ->
    				builder.configureGraphQl((graphQlBuilder) ->
    						graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider()));
    		}
    
    }
  2. Understand Selection Sets vs Spring Data Projections

    main

    Spring 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 @Value and 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.
  3. Auto-register Querydsl repositories with @GraphQlRepository

    main

    If you annotate a repository with @GraphQlRepository, Spring for GraphQL will automatically register it for queries that:

    1. Do not already have a registered DataFetcher.
    2. 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 typeName attribute of @GraphQlRepository to override this.
    • Pagination: For paginated queries, the domain type name must match the Connection type name without the Connection suffix (e.g., Book matches BooksConnection). Auto-registered pagination is offset-based with 20 items per page.
    • Customization: If the repository implements QuerydslBinderCustomizer or ReactiveQuerydslBinderCustomizer, these customizations are automatically applied.
    • Boot Starter: The Spring Boot starter automatically detects these beans and initializes the RuntimeWiringConfigurer.
  4. How GraphQlClient and its transport extensions work

    main

    Spring for GraphQL provides a GraphQlClient interface 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: Uses RestClient for blocking (synchronous) HTTP requests.
    • HttpGraphQlClient: Uses WebClient for 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: Uses RSocketRequester for RSocket requests. Like WebSockets, it is connection-oriented and multiplexed.

    All these clients use a Builder to configure transport-specific options, while inheriting common options from a base Builder (such as DocumentSource strategies and interceptors).

  5. Customize GraphQL Observation Metadata

    main

    If 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)
  6. Apply fine-grained security to GraphQL data fetching

    main

    To implement field-level or data-specific security, apply Spring Security annotations like @PreAuthorize or @Secured directly 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.

  7. Handle Exceptions in Spring GraphQL

    main

    Spring for GraphQL provides several ways to handle exceptions:

    1. DataFetcherExceptionResolver: Used to resolve exceptions occurring during data fetching into a list of graphql.GraphQLErrors. The Boot starter detects these beans automatically. You can extend DataFetcherExceptionResolverAdapter for convenience.
    2. @GraphQlExceptionHandler: Part of the annotated controller model, allowing you to handle exceptions with specific method signatures.
    3. SubscriptionExceptionResolver: Specifically for resolving exceptions that occur within a subscription Publisher.
    4. 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 to INTERNAL_ERROR with an opaque message to avoid leaking implementation details.

  8. Choose between @BatchMapping and BatchLoaderRegistry

    main

    When loading related entities, you have two primary options:

    1. @BatchMapping: A high-level shortcut that minimizes boilerplate. It is recommended for most straightforward use cases.
    2. 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 GraphQLContext used by @BatchMapping methods.
    • Ensures proper Context Propagation to the batch loading functions.

    While you can register DataLoaders manually, doing so bypasses these automatic benefits.

  9. Use Annotated Controllers for GraphQL Data Fetching

    main

    Spring for GraphQL uses an annotation-based programming model where @Controller beans act as data fetching components. Methods within these controllers are mapped to GraphQL fields using annotations, which the AnnotatedControllerConfigurer then registers as DataFetchers via RuntimeWiring.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!";
        }
    }
  10. Optimize Batch Loading strategies

    main

    Because 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 @SchemaMapping method.

    • 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 DataLoader key (e.g., a key composed of both the Person and the Filter).

    • 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.