Resolver & Data Fetcher
Schema-Felder mit @QueryMapping und @SchemaMapping an Java binden - und wie Spring for GraphQL ein verschachteltes Query-Feld Feld für Feld auflöst.
Deutsche Übersetzung in Arbeit
Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.
Auf dieser Seite
The schema says what data exists; a resolver (in graphql-java terms, a DataFetcher) says how to
fetch it. Spring for GraphQL lets you write resolvers as annotated controller methods that look a lot like
@RestController handlers. The engine's job is to walk the client's query and call the right resolver for each
field - understanding that walk is the key to using GraphQL well.
@QueryMapping: the entry points
A method annotated @QueryMapping resolves a field on the root Query type. The method name matches the
schema field:
@Controller
class BookController {
private final BookService books;
@QueryMapping
Book book(@Argument Long id) { // resolves Query.book(id: ID!)
return books.findById(id);
}
@QueryMapping
List<Book> books(@Argument String topic) { // resolves Query.books(topic: String)
return books.search(topic);
}
}@Argument binds a GraphQL query argument to a method parameter. This is the entry point: when a client sends
query { book(id: 42) { ... } }, the engine calls book(42) to get the root Book object.
@SchemaMapping: resolving nested fields
Here's the crucial insight: GraphQL resolves a query field by field. Once the engine has the Book, it
needs to resolve each requested sub-field. Scalar fields (title, isbn) come straight off the object via
getters - no code needed. But a field that requires work - like fetching the author from another service -
gets its own resolver:
@SchemaMapping // resolves Book.author
Author author(Book book) { // the parent Book is passed in
return authorService.findById(book.authorId());
}The method's parameter is the parent object (the Book whose author is being resolved), and Spring
matches the method to Book.author by the return type and the containing type. So for the query:
query { book(id: 42) { title author { name } } }the engine calls book(42) → gets a Book → reads title off it directly → calls author(book) to resolve
the nested Author → reads name off that. It's a traversal of the query tree, one resolver per non-trivial
field.
Not every field needs a resolver
If a Book object already has the field as a property (title, isbn, availableCopies), Spring reads it via the getter automatically - you write no code. You only add an @SchemaMapping when resolving a field takes real work: a separate service call, a database lookup, a computed value. Start with just @QueryMapping and add field resolvers only where needed.
Why per-field resolution is powerful (and dangerous)
This design is what makes GraphQL flexible: the client picks any subset of fields, and the engine calls only the
resolvers for the fields actually requested. Ask for { title } and author(book) never runs. Ask for
{ title author { name } } and it does.
But it has a sharp edge. Consider a list query:
query { books(topic: "java") { title author { name } } }The engine calls books(...) once → gets 20 books → then calls author(book) once per book = 20 more
calls. That's the N+1 problem, and per-field resolution makes it the default behaviour. The next lesson is
entirely about taming it.
The query is you pointing at things in a museum: 'that painting - and who painted it - and what else did they paint?' The engine is a guide who fetches exactly what you point at, in order. Scalar fields are placards already on the wall (read instantly). An @SchemaMapping is the guide walking to the archive to look up the artist because you asked about them - real effort, done only because you pointed. Point at twenty paintings and ask each artist, and the guide makes twenty archive trips: flexible, but you can see how it could get slow.
For the query query { book(id: 7) { title reviews { rating member { name } } } }, and given @QueryMapping
book(id), @SchemaMapping reviews(Book), and @SchemaMapping member(Review), list the resolver calls the engine
makes if book 7 has 3 reviews. Which fields need no resolver?
How does Spring for GraphQL resolve a nested field like Book.author?
Key takeaways
- A resolver (DataFetcher) says how to fetch a schema field; in Spring you write them as @Controller methods.
- @QueryMapping resolves root Query fields; @Argument binds query arguments to method parameters.
- @SchemaMapping resolves a nested field and receives the parent object; Spring matches it by containing type and return type.
- Plain properties (title, isbn) need no resolver - Spring reads them via getters; add @SchemaMapping only for fields that take real work.
- GraphQL resolves field by field across the query tree, running only the resolvers for requested fields - flexible, but the per-field model makes N+1 the default for lists.