Write efficient resolvers with DataLoader, batching, and N+1 prevention
View on GitHubpluginagentmarketplace/custom-plugin-graphql
developer-roadmap
January 20, 2026
Select agents to install to:
npx add-skill https://github.com/pluginagentmarketplace/custom-plugin-graphql/blob/main/skills/graphql-resolvers/SKILL.md -a claude-code --skill graphql-resolversInstallation paths:
.claude/skills/graphql-resolvers/# GraphQL Resolvers Skill
> Build performant data fetching with proper patterns
## Overview
Master resolver implementation including the critical DataLoader pattern for preventing N+1 queries, context design, and error handling strategies.
---
## Quick Reference
| Pattern | Purpose | When to Use |
|---------|---------|-------------|
| DataLoader | Batch + cache | Any relationship field |
| Context | Request-scoped data | Auth, loaders, datasources |
| Field resolver | Computed fields | Derived data |
| Root resolver | Entry points | Query/Mutation fields |
---
## Core Patterns
### 1. Resolver Signature
```javascript
// (parent, args, context, info) => result
const resolvers = {
Query: {
// Root resolver - parent is undefined
user: async (_, { id }, { dataSources }) => {
return dataSources.users.findById(id);
},
},
User: {
// Field resolver - parent is User object
posts: async (user, { first = 10 }, { loaders }) => {
return loaders.postsByAuthor.load(user.id);
},
// Computed field - sync is fine
fullName: (user) => `${user.firstName} ${user.lastName}`,
// Default resolver (implicit)
// email: (user) => user.email,
},
};
```
### 2. DataLoader Pattern
```javascript
const DataLoader = require('dataloader');
// N+1 Problem:
// Query: { users { posts { title } } }
// Without DataLoader: 1 + N queries
// Solution: Batch loading
const createLoaders = () => ({
// Batch by foreign key
postsByAuthor: new DataLoader(async (authorIds) => {
// 1. Single query for all authors
const posts = await db.posts.findAll({
where: { authorId: { [Op.in]: authorIds } }
});
// 2. Group by author
const postsByAuthor = {};
posts.forEach(post => {
if (!postsByAuthor[post.authorId]) {
postsByAuthor[post.authorId] = [];
}
postsByAuthor[post.authorId].push(post);
});
// 3. Return in same order as input
return authorIds.map(id => postsByAuthor[id] ||