Skip to main content Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-graphql --skill graphql-resolversEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Explorador de archivos
6 archivosOcupaciones relacionadasSOC
Basado en la clasificación ocupacional SOC
| name | graphql-resolvers |
| description | Write efficient resolvers with DataLoader, batching, and N+1 prevention |
| sasmp_version | 1.3.0 |
| bonded_agent | 03-graphql-resolvers |
| bond_type | PRIMARY_BOND |
| version | 2.0.0 |
| complexity | intermediate |
| estimated_time | 4-6 hours |
| prerequisites | ["graphql-fundamentals","graphql-schema-design"] |
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
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.users.findById(id);
},
},
User: {
posts: async (user, { first = 10 }, { loaders }) => {
return loaders.postsByAuthor.load(user.id);
},
fullName: (user) => `${user.firstName} ${user.lastName}`,
},
};
2. DataLoader Pattern
const DataLoader = require('dataloader');
const createLoaders = () => ({
postsByAuthor: new DataLoader(async (authorIds) => {
const posts = await db.posts.findAll({
where: { authorId: { [Op.in]: authorIds } }
});
const postsByAuthor = {};
posts.forEach(post => {
if (!postsByAuthor[post.authorId]) {
postsByAuthor[post.authorId] = [];
}
postsByAuthor[post.authorId].push(post);
});
return authorIds.map(id => postsByAuthor[id] || []);
}),
userById: new DataLoader(async (ids) => {
const users = await db.users.findAll({
where: { id: { [Op.in]: ids } }
});
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) || null);
}),
});
const resolvers = {
Post: {
author: (post, _, { loaders }) => {
return loaders.userById.load(post.authorId);
},
},
User: {
posts: (user, _, { loaders }) => {
return loaders.postsByAuthor.load(user.id);
},
},
};
3. Context Setup
const createContext = async ({ req, res }) => {
const token = req.headers.authorization?.replace('Bearer ', '');
const user = token ? await verifyToken(token) : null;
const loaders = createLoaders();
const dataSources = {
users: new UserDataSource(db),
posts: new PostDataSource(db),
};
const requestId = req.headers['x-request-id'] || crypto.randomUUID();
return {
user,
loaders,
dataSources,
requestId,
};
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: createContext,
});
4. Error Handling
import { GraphQLError } from 'graphql';
const resolvers = {
Mutation: {
createUser: async (_, { input }, { dataSources, user }) => {
if (!user) {
throw new GraphQLError('Not authenticated', {
extensions: { code: 'UNAUTHENTICATED' }
});
}
const validationErrors = validateInput(input);
if (validationErrors.length > 0) {
return { user: null, errors: validationErrors };
}
try {
const newUser = await dataSources.users.create(input);
return { user: newUser, errors: [] };
} catch (error) {
if (error.code === 'DUPLICATE_EMAIL') {
return {
user: null,
errors: [{ field: 'email', message: 'Already exists' }]
};
}
throw new GraphQLError('Internal error', {
extensions: { code: 'INTERNAL_ERROR' }
});
}
},
},
};
5. Subscription Resolvers
import { PubSub, withFilter } from 'graphql-subscriptions';
const pubsub = new PubSub();
const resolvers = {
Mutation: {
sendMessage: async (_, { input }, { dataSources }) => {
const message = await dataSources.messages.create(input);
pubsub.publish('MESSAGE_SENT', {
messageSent: message,
channelId: input.channelId,
});
return message;
},
},
Subscription: {
userCreated: {
subscribe: () => pubsub.asyncIterator(['USER_CREATED']),
},
messageSent: {
subscribe: withFilter(
() => pubsub.asyncIterator(['MESSAGE_SENT']),
(payload, variables) => {
return payload.channelId === variables.channelId;
}
),
},
},
};
Performance Targets
| Resolver Type | Target | Action if Exceeded |
|---|
| Simple field | < 10ms | Check DB indexes |
| DataLoader batch | < 50ms | Optimize query |
| Complex computation | < 200ms | Consider caching |
| Total request | < 500ms | Profile and optimize |
Troubleshooting
| Issue | Symptom | Solution |
|---|
| N+1 queries | Slow, many DB calls | Use DataLoader |
| Memory leak | Growing memory | Create loaders per request |
| Stale data | Wrong results | Clear DataLoader cache |
| Race condition | Intermittent errors | Don't mutate context |
Debug Techniques
const loader = new DataLoader(async (keys) => {
console.log(`Batching ${keys.length} keys`);
});
const withTiming = (resolver) => async (...args) => {
const start = Date.now();
const result = await resolver(...args);
console.log(`Took ${Date.now() - start}ms`);
return result;
};
const loggingPlugin = {
requestDidStart() {
const start = Date.now();
return {
willSendResponse() {
console.log(`Request took ${Date.now() - start}ms`);
},
};
},
};
Usage
Skill("graphql-resolvers")
Related Skills
graphql-schema-design - Schema that resolvers implement
graphql-apollo-server - Server configuration
graphql-security - Auth in resolvers
Related Agent
03-graphql-resolvers - For detailed guidance