Skip to main content التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
يتجاوز الأمر المباشر Prompt المخصّص للمراجعة. افحص المصدر قبل تشغيله.
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-graphql --skill graphql-resolversيبقى الأمر في سطر واحد. مرّر أفقيًا لمراجعته كاملًا قبل النسخ.
تفضّل نسخة محلية؟ نزّل الملفات المتاحة حاليًا لدى SkillsMP.
المهن ذات الصلةSOC
استنادا إلى تصنيف 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