Spring Boot: Getting Started with GraphQL
|
This section documents the current GraphQL specification (October 2021), plus the working draft for
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as GraphQL tooling iterates quickly. |
Spring for GraphQL is Spring’s own integration layer on top of the reference graphql-java engine, and
spring-boot-starter-graphql is what turns that integration into a handful of auto-configured beans and one
working /graphql endpoint. This page covers adding the starter, where schema files live, what gets
auto-configured, enabling GraphiQL for local development, the spring.graphql.* configuration surface, and
how to inspect or print the schema Spring assembled.
Adding spring-boot-starter-graphql
A single starter pulls in Spring for GraphQL, the graphql-java engine it wraps, and (transitively) Spring
Web — so it works alongside either the servlet stack (spring-boot-starter-web) or the reactive stack
(spring-boot-starter-webflux), picking whichever transport is on the classpath at auto-configuration time:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
No version is declared explicitly here because Spring Boot’s dependency management (the parent POM or BOM)
pins it — this page targets Spring for GraphQL 2.0.x as shipped alongside Spring Boot 4.x / Spring Framework
7.x, on a Java 17+ baseline, without pinning a specific patch release. Adding
spring-boot-starter-websocket alongside the above additionally enables the WebSocket transport used for
subscriptions, covered in
Spring Boot: Transports, Security &
Testing.
How Spring for GraphQL sits on top of graphql-java
graphql-java is the low-level reference implementation of the GraphQL specification for the JVM: it parses a
schema document, validates and executes operations against it, and calls whatever DataFetcher a field is
wired to — but it has no opinion on Spring, dependency injection, or HTTP. Spring for GraphQL is the layer that
makes graphql-java feel native to a Spring application:
-
It builds the
graphql.GraphQLinstance for you from schema files on the classpath instead of hand-assembledRuntimeWiring. -
It replaces manually registered
DataFetcherlambdas with annotated@Controllermethods (@QueryMapping/@MutationMapping/@SubscriptionMapping/@SchemaMapping), the subject of Spring Boot: Controllers. -
It exposes the transport plumbing — HTTP, WebSocket, RSocket — as auto-configured Spring beans rather than something the application wires up by hand.
-
It integrates Spring’s own request-scoped context (
Principal,@ContextValue) andDataLoaderbatching (BatchLoaderRegistry, covered in Spring Boot: Data Loading & Integration) into `graphql-java’s per-execution context.
Every concept from earlier pages in this section — the schema, resolvers, the request lifecycle — still applies unchanged; Spring for GraphQL only changes how those pieces are declared and wired together on a Spring Boot application. See Spring for GraphQL — Boot Starter for the full auto-configuration reference this page summarizes.
Schema files under src/main/resources/graphql/**.graphqls
By default, the starter looks for schema files anywhere under src/main/resources/graphql/, matching the
.graphqls (or .graphql) extension, and merges every file it finds into a single schema — so a schema is
commonly split by concern (schema.graphqls, book.graphqls, author.graphqls) rather than kept in one large
file:
# src/main/resources/graphql/schema.graphqls
type Query {
book(id: ID!): Book
books: [Book!]!
}
type Mutation {
addBook(input: AddBookInput!): Book!
}
type Book {
id: ID!
title: String!
publishedYear: Int
author: Author!
}
type Author {
id: ID!
name: String!
books: [Book!]!
}
input AddBookInput {
title: String!
authorId: ID!
}
The lookup location is itself a configuration property (spring.graphql.schema.locations, see below), so a
project can point at a different classpath directory, or several, if it needs to. `graphql-java’s own SDL
grammar and type system are exactly what Schema & Type System
already covers — nothing about the grammar changes when the file is read by Spring’s auto-configuration instead
of hand-parsed.
Auto-configuration: GraphQlSource and ExecutionGraphQlService
Two beans anchor everything the starter wires up. GraphQlSource is responsible for building the executable
graphql.GraphQL instance — parsing the merged schema files, applying any RuntimeWiringConfigurer beans
(including the ones that register @Controller handler methods as DataFetcher`s), and exposing the resulting
schema for both execution and introspection. `ExecutionGraphQlService is the transport-agnostic entry point
that actually executes a request against that GraphQL instance: it accepts a GraphQlRequest, runs it through
the interceptor chain described below, and returns a GraphQlResponse — independent of whether the request
arrived over HTTP, WebSocket, or RSocket.
Everything above ExecutionGraphQlService is transport-specific and equally auto-configured. On the servlet
stack (spring-boot-starter-web on the classpath), Spring Boot registers a GraphQlHttpHandler behind an
HttpGraphQlHttpHandler-backed servlet mapping; on the reactive stack (spring-boot-starter-webflux), the
equivalent RouterFunction is registered instead. Either way, an application gets a working POST /graphql
endpoint with no explicit @Controller or @Bean of its own — only schema files and, later, resolver methods
are the application’s own responsibility. Serving GraphQL over
HTTP covers the wire format this endpoint speaks; this page only covers how that endpoint comes to exist.
Trying the endpoint
With only the schema file above on the classpath and no resolver written yet, the auto-configured endpoint
already exists and already validates requests against that schema — it just has no DataFetcher behind
Query.book yet, so a syntactically valid query against a field the schema doesn’t declare fails validation
before execution, while a query against a real field with no resolver returns null for that field rather than
an error:
curl -s http://localhost:8080/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ book(id: \"1\") { title } }"}'
{
"data": {
"book": null
}
}
Spring Boot: Controllers is what fills in that null with a
real @QueryMapping method; this page’s endpoint is otherwise already indistinguishable from a hand-wired one
once a resolver exists.
Enabling GraphiQL
The starter bundles GraphiQL (introduced in Getting Started) but
ships it disabled by default. Turning it on for local development exposes an in-browser query editor at
/graphiql, pre-pointed at the application’s own /graphql endpoint:
spring:
graphql:
graphiql:
enabled: true
path: /graphiql # default shown; override if it collides with another route
A production deployment typically leaves this false (or gates the path behind authentication) for the same
reason Serving GraphQL over HTTP gives for disabling any bundled
IDE on a public endpoint: an interactive schema browser widens the attack surface covered in
Security and Demand Control.
spring.graphql.* configuration properties
The properties an application reaches for most often when getting a server running:
| Property | Purpose |
|---|---|
|
The HTTP endpoint path. Defaults to |
|
Classpath location(s) to scan for |
|
File extensions treated as schema files. Defaults to |
|
Exposes the assembled schema’s SDL over HTTP for inspection (see below). Defaults to |
|
Enables the bundled GraphiQL IDE and its path, as shown above. Defaults to |
|
Enables the WebSocket transport (subscriptions) at the given path when set; unset by default. |
|
CORS configuration scoped to the GraphQL endpoint specifically, mirroring ordinary Spring MVC/WebFlux CORS properties. |
|
Whether the schema can be queried via introspection at runtime, covered next. Defaults to |
|
How long a WebSocket transport waits for the subscription protocol’s |
|
A Spring Boot Actuator property (not |
spring:
graphql:
path: /graphql
schema:
locations: classpath:graphql/**/
printer:
enabled: true
graphiql:
enabled: true
cors:
allowed-origins: "https://app.example.com"
allowed-methods: POST
See Spring for GraphQL — Boot Starter for the complete, versioned property list — this table covers only what a project reaches for while first setting a server up.
Schema inspection and printing
Two related but distinct capabilities help verify that the schema Spring assembled is what the application
intended. Schema printing (spring.graphql.schema.printer.enabled=true) exposes the fully merged SDL — exactly as graphql-java sees it after combining every *.graphqls file — at POST to the endpoint path with
an empty query and the getSchema extension, or more simply via management.endpoint.graphql.enabled=true
under Spring Boot Actuator, which surfaces it as an ordinary management endpoint. Printing the live schema back
out is the fastest way to confirm that every file under spring.graphql.schema.locations was actually picked
up and merged as expected.
Schema inspection, separately, runs at application startup: Spring for GraphQL’s SchemaMappingInspector
walks the assembled schema and cross-checks it against the registered @Controller handler methods, logging a
warning for any schema field that has no corresponding DataFetcher (and, conversely, flagging Java
return-type mismatches it can detect statically). This is the fastest way to catch a typo in a
`@SchemaMapping’s field name, or a schema field nobody wired a resolver for, before a client ever hits it at
runtime rather than after. Both mechanisms are covered in more operational depth at
Spring for GraphQL — Request
Execution.
Runtime introspection (spring.graphql.schema.introspection.enabled) is a separate, client-facing capability
from either of the above — it is the schema/type mechanism
Introspection already covers, left enabled by default so tools like
GraphiQL can discover the schema on their own. Turning it off outside development is one of several defenses
covered in Security and Demand Control, not something
specific to getting a server running in the first place.
The request pipeline
Once the starter has wired all of the above together, an HTTP (or WebSocket) request flows through the same fixed pipeline regardless of which transport carried it in:
The transport layer (the GraphQlHttpHandler/RouterFunction auto-configured above) decodes the incoming
request into a transport-neutral WebGraphQlRequest. Every request then passes through the
WebGraphQlInterceptor chain — an application’s own hook for cross-cutting concerns such as authentication
context propagation or request logging, applied uniformly whether the request arrived over HTTP or WebSocket.
ExecutionGraphQlService hands the request to the underlying graphql-java engine, which parses, validates,
and executes it exactly as Getting Started's request-lifecycle
diagram describes, invoking each field’s DataFetcher — in a Spring for GraphQL application, almost always
an @Controller method annotated @QueryMapping/@MutationMapping/@SubscriptionMapping/@SchemaMapping,
the subject of the next page. Any of those methods that need to batch per-field lookups register with the
BatchLoaderRegistry, which Spring Boot:
Data Loading & Integration covers as the JVM’s answer to the N+1 problem introduced in
Performance and N+1.
Related pages
-
Getting Started — the language-agnostic request lifecycle this page’s pipeline diagram specializes for Spring for GraphQL.
-
Spring Boot: Controllers — writing the
@QueryMapping/@MutationMapping/@SubscriptionMapping/@SchemaMappingmethods that back the schema introduced here. -
Spring Boot: Data Loading & Integration —
@BatchMapping,BatchLoaderRegistry, and Spring Data integration. -
Spring Boot: Transports, Security & Testing — the WebSocket/RSocket transports, CSRF, and testing support this getting-started page does not cover.
-
REST APIs with Spring MVC and WebFlux — the equivalent auto-configuration story for a REST endpoint on the same servlet/reactive stacks.