Resolver Functions
Understand resolver functions — how they map schema fields to actual data sources.
Resolver Functions
Resolvers are the bridge between your schema and your data. Every field in your schema has a corresponding resolver function that returns the data for that field. Let's learn how they work!
How Resolvers Work
When a query comes in, Apollo Server walks through the query and calls the resolver for each field. Think of it as a chain:
Query → Resolver → Data
Basic Resolvers
Let's set up sample data and resolvers for our game review API:
// Sample data (in a real app, this would come from a database)
let games = [
{ id: "1", title: "Zelda: Tears of the Kingdom", platform: ["Switch"] },
{ id: "2", title: "Final Fantasy XVI", platform: ["PS5", "PC"] },
{ id: "3", title: "Elden Ring", platform: ["PS5", "Xbox", "PC"] },
];
let authors = [
{ id: "1", name: "Mario", verified: true },
{ id: "2", name: "Yoshi", verified: false },
{ id: "3", name: "Peach", verified: true },
];
let reviews = [
{ id: "1", rating: 9, content: "Amazing game!", game_id: "1", author_id: "1" },
{ id: "2", rating: 10, content: "A masterpiece!", game_id: "2", author_id: "2" },
{ id: "3", rating: 7, content: "Pretty good", game_id: "3", author_id: "3" },
];
Query Resolvers
Query resolvers handle the top-level entry points:
const resolvers = {
Query: {
games() {
return games;
},
game(_, args) {
return games.find(game => game.id === args.id);
},
reviews() {
return reviews;
},
review(_, args) {
return reviews.find(review => review.id === args.id);
},
authors() {
return authors;
},
author(_, args) {
return authors.find(author => author.id === args.id);
},
},
};
Resolver Arguments
Every resolver function receives four arguments:
function resolver(parent, args, context, info) {
// parent — the return value of the parent resolver
// args — arguments passed to the field in the query
// context — shared data (auth, DB connection, etc.)
// info — metadata about the query execution
}
| Argument | Description | Common Use |
|---|---|---|
parent | Result from the parent resolver | Accessing related data |
args | Arguments passed to the field | Filtering by ID, etc. |
context | Shared across all resolvers | Auth, DB pools |
info | Query execution metadata | Advanced optimization |
Relationship Resolvers
This is where GraphQL gets powerful. We can resolve relationships between types:
const resolvers = {
Query: {
// ... query resolvers from above
},
// Resolve the 'reviews' field on Game type
Game: {
reviews(parent) {
return reviews.filter(r => r.game_id === parent.id);
},
},
// Resolve the 'game' and 'author' fields on Review type
Review: {
game(parent) {
return games.find(g => g.id === parent.game_id);
},
author(parent) {
return authors.find(a => a.id === parent.author_id);
},
},
// Resolve the 'reviews' field on Author type
Author: {
reviews(parent) {
return reviews.filter(r => r.author_id === parent.id);
},
},
};
Testing Nested Queries
Now you can run deeply nested queries:
{
game(id: "1") {
title
platform
reviews {
rating
content
author {
name
verified
}
}
}
}
Default Resolvers
You don't need to write resolvers for every single field. Apollo uses default resolvers that return the property with the same name from the parent object. For example, if your data has a title property, the title field resolver is created automatically.
Key Takeaways
- Resolvers map schema fields to actual data
- Each resolver receives four arguments: parent, args, context, info
- The
parentargument is key for resolving relationships - Default resolvers handle simple property lookups automatically
- Relationship resolvers connect types together
Next up: Query Variables — making queries dynamic and reusable!