Resolvers in GraphQL — Complete Guide
In this tutorial, you will learn about Resolvers in GraphQL. We cover key concepts, practical examples, and best practices to help you master this topic.
Resolvers are functions that provide values for schema fields. Each field in a GraphQL schema maps to a resolver function. The resolver receives the parent value, arguments, context, and an info object to compute the field value.
What You'll Learn
- Resolver function signature (parent, args, context, info)
- Field-level vs type-level resolve patterns
- Using context for data sources and authentication
- Resolver chaining and the default resolver behavior
Why It Matters
Resolvers are the implementation layer that connects your schema to actual data sources. Efficient resolver design determines API performance. Poor resolvers cause N+1 problems and slow response times.
Real-World Use
Apollo Server uses data sources that are added to context for Dependency Injection. Each resolver calls dataSource methods that handle Caching and deduplication internally.
flowchart TD
Query[Root Query] --> ResolverA[Resolver: books]
ResolverA --> DataSource[Data Source: BookAPI]
DataSource --> DB[(Database)]
ResolverA --> Book{Book type}
Book --> ResolverB[Default Resolver: title]
Book --> ResolverC[Resolver: author]
ResolverC --> DataSource2[Data Source: AuthorAPI]
Teacher Mindset
Every resolver is a micro-function with one job: return the value for its field. Default resolvers automatically look up a property with the same name on the parent object. Only write explicit resolvers when you need custom logic.
Code Examples
// Example 1: Resolver signatures
const resolvers = {
Query: {
book: (parent, args, context, info) => {
return context.dataSources.books.getById(args.id);
},
books: (parent, args, context) => {
return context.dataSources.books.list();
}
},
Book: {
author: (parent, args, context) => {
return context.dataSources.authors.getById(parent.authorId);
}
}
};
// Example 2: Context with data sources and auth
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
const token = req.headers.authorization || '';
const user = await verifyToken(token);
return {
user,
dataSources: {
books: new BookAPI(),
authors: new AuthorAPI()
}
};
}
});
// Example 3: Batch resolver with DataLoader
const bookResolvers = {
Query: {
books: async (_, __, { dataLoaders }) => {
return dataLoaders.books.loadMany(bookIds);
}
}
};
// Default resolver - no explicit function needed for simple fields
// Schema: type Book { id: ID! title: String! }
// The title field automatically reads parent.title
Common Mistakes
- Writing resolvers that ignore the parent argument for relational data
- Fetching data inside field resolvers without batching, causing N+1 queries
- Not using the context argument for shared dependencies
- Throwing generic errors instead of specific GraphQLError types
- Forgetting that top-level fields on the Query type receive root as parent
Practice
- Write a resolver for Query.books that fetches from a REST API.
- Implement a field resolver for Book.author that uses the parent authorId.
- Create a context that provides authentication and data sources.
- Use args to filter results in a resolver function.
- Challenge: Implement a resolver chain three levels deep and trace the parent values at each level.
FAQ
Mini Project
Implement resolvers for your library schema. Write resolvers for Query.books, Query.book(id), Book.author, Mutation.createBook. Use context to pass a database client or in-memory data store.
What's Next
Next, you will learn about input types for structuring complex mutation arguments in GraphQL.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro