GraphQL with Spring Boot
GraphQL is a query language for APIs and a runtime that fulfils those queries against your data. Instead of many fixed REST endpoints, GraphQL exposes a single endpoint backed by a strongly typed schema, and the client asks for exactly the fields it needs. Spring Boot integrates GraphQL through the Spring for GraphQL project and the spring-boot-starter-graphql starter, which builds on GraphQL Java.
Why GraphQL?
REST works well, but it has two recurring friction points. Over-fetching happens when an endpoint returns more data than the client needs (a mobile screen pulling a full User just to show a name). Under-fetching is the opposite: one screen needs data from several resources, forcing multiple round trips (/users/1, then /users/1/orders, then /orders/9/items).
GraphQL solves both by letting the client describe its data requirements in a single request. The server returns precisely that shape, no more and no less.
query {
book(id: "1") {
title
author { name }
}
}
GraphQL vs REST
| Aspect | REST | GraphQL |
|---|---|---|
| Endpoints | Many (/users, /orders, …) | One (/graphql) |
| Data shape | Fixed per endpoint | Chosen by the client |
| Over/under-fetching | Common | Avoided by design |
| Typing | Implicit / OpenAPI add-on | Built-in typed schema |
| HTTP verbs | GET/POST/PUT/DELETE | Usually POST (queries & mutations) |
| Versioning | URL/header versions | Evolve schema, deprecate fields |
| Caching | HTTP caching is easy | Needs app-level caching |
Note: GraphQL is not a replacement for REST. REST shines for simple resource CRUD, file transfer, and HTTP-cache-friendly reads. GraphQL excels when clients are diverse and aggregate related data. See Building REST APIs for the REST approach.
Adding the starter
Spring for GraphQL is one dependency. Add the GraphiQL-friendly web starter alongside it (Spring MVC here; WebFlux is also supported).
<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>
The starter auto-configures a GraphQL HTTP endpoint at /graphql that accepts POST requests. You do not register any controller mapping for it; Spring for GraphQL wires it up automatically once a schema is present.
Your first schema
Spring for GraphQL is schema-first. You define the API contract in a .graphqls file placed under src/main/resources/graphql/. Files there are discovered automatically.
# src/main/resources/graphql/schema.graphqls
type Query {
book(id: ID!): Book
}
type Book {
id: ID!
title: String!
pages: Int
}
A matching resolver (a Spring @Controller) supplies the data for each schema field. The @QueryMapping method name maps to the book query field.
@Controller
public class BookController {
@QueryMapping
public Book book(@Argument String id) {
return new Book(id, "Clean Code", 464);
}
}
public record Book(String id, String title, int pages) { }
Tip: Records make excellent GraphQL types. Their accessor methods line up with schema fields, and they are immutable by default. See Java records.
The /graphql endpoint
Clients POST a JSON envelope containing the query string. The response always uses the data (and optionally errors) envelope.
curl -X POST http://localhost:8080/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ book(id: \"1\") { title pages } }"}'
Output:
{
"data": {
"book": {
"title": "Clean Code",
"pages": 464
}
}
}
GraphiQL — the in-browser IDE
GraphiQL is an interactive query editor with schema documentation and autocompletion. It is bundled with the starter but disabled by default. Enable it in your configuration:
spring.graphql.graphiql.enabled=true
# optional: change the default path (/graphiql)
spring.graphql.graphiql.path=/graphiql
spring:
graphql:
graphiql:
enabled: true
schema:
printer:
enabled: true # exposes the assembled schema at /graphql/schema
With the app running, open http://localhost:8080/graphiql. You can explore the schema, build queries with autocomplete, and run them against your live server.
Warning: GraphiQL is a development tool. Leave
spring.graphql.graphiql.enabledoff (or guard it behind a profile) in production so you do not expose an interactive console publicly. See Profiles.
How a request flows
- The client POSTs a query to
/graphql. - Spring for GraphQL parses and validates it against the schema.
- For each requested field, the matching
@QueryMapping/@SchemaMappingresolver runs. - Results are assembled into the
datashape the client asked for. - Any errors are collected into the
errorsarray of the response.
This per-field resolution is what makes the next two pages important: defining the schema and writing resolvers, then handling writes and real-time updates.
In This Section
- Schema & Resolvers —
.graphqlsschemas,@QueryMapping,@SchemaMappingfor nested fields,@Argumentbinding, and solving the N+1 problem with@BatchMapping. - Queries, Mutations & Subscriptions — writing queries,
@MutationMappingfor writes, input types,@SubscriptionMappingwithFlux, validation, and error handling.