用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tools-only/X-Skills --skill build-graphql-server命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
基于 SOC 职业分类
正在显示 SKILL.md
| name | build-graphql-server |
| description | Build a GraphQL server with schema-first design |
| shortcut | gql |
Create a production-ready GraphQL server with type-safe schemas, optimized resolvers, real-time subscriptions, authentication, and comprehensive tooling using schema-first design principles.
Use /build-graphql-server when you need to:
DON'T use this when:
This command implements Apollo Server with DataLoader as the primary approach because:
Alternative considered: GraphQL Yoga
Alternative considered: Mercurius (Fastify)
Before running this command:
Create comprehensive type definitions with proper nullability and relationships.
Build efficient resolvers with DataLoader for batching and caching.
Implement context-based auth with field-level permissions.
Configure WebSocket server for real-time updates.
Add query complexity analysis, depth limiting, and caching.
The command generates:
schema/ - GraphQL schema definitionsresolvers/ - Resolver implementationsdataloaders/ - DataLoader configurationsdirectives/ - Custom schema directivesserver.js - Apollo Server setupgenerated/types.ts - TypeScript definitionstests/ - Integration and unit tests// schema/schema.graphql
scalar DateTime
scalar Upload
directive @auth(requires: Role = USER) on FIELD_DEFINITION
directive @rateLimit(max: Int, window: String) on FIELD_DEFINITION
directive @deprecated(reason: String) on FIELD_DEFINITION | ENUM_VALUE
enum Role {
USER
MODERATOR
ADMIN
}
type User {
id: ID!
username: String!
email: String!
role: Role!
profile: Profile
posts(
first: Int
after: String
orderBy: PostOrderBy
filter: PostFilter
): PostConnection!
followers: [User!]!
following: [User!]!
createdAt: DateTime!
: !
}
type {
:
:
:
:
}
type {
: !
: !
: !
:
: !
: [!]!
(: , : ): !
: !
: !
: !
:
: !
: !
}
type {
: !
: !
: !
: !
:
: [!]!
: !
: !
: !
}
type {
: !
: !
: !
(: , : ): !
}
# -style pagination types
type {
: [!]!
: !
: !
}
type {
: !
: !
}
type {
: [!]!
: !
: !
}
type {
: !
: !
}
type {
: !
: !
:
:
}
# types
input {
: !
: !
: !
:
}
input {
:
:
:
:
}
input {
: !
: !
: [!]
:
}
input {
:
:
: [!]
:
}
input {
:
:
: [!]
:
}
enum {
}
# types
type {
# queries
: @auth
(: !):
(
:
:
:
): !
# queries
(: !):
(
:
:
:
:
): ! @(: , : )
(: !, : ): [!]!
(: = ): [!]!
# queries
(: !):
: [!]!
}
type {
#
(: !): !
(: !, : !): !
: ! @auth
# mutations
(: !): ! @auth
(: !): ! @auth
(: !): ! @auth
# mutations
(: !): ! @auth
(: !, : !): ! @auth
(: !): ! @(: )
(: !): ! @auth
# mutations
(: !, : !, : ): ! @auth
(: !, : !): ! @auth
(: !): ! @auth
}
type {
# subscriptions
(: ): !
(: !): !
: !
# subscriptions
(: !): !
(: !): !
# activity
(: !): !
}
type {
: !
: !
}
type {
: !
: !
:
}
{ } = ();
{ expressMiddleware } = ();
{ } = ();
{ makeExecutableSchema } = ();
{ } = ();
{ useServer } = ();
express = ();
http = ();
cors = ();
= ();
depthLimit = ();
costAnalysis = ();
typeDefs = ();
resolvers = ();
{ authDirective } = ();
{ rateLimitDirective } = ();
schema = ({
typeDefs,
resolvers
});
schema = (schema, );
schema = (schema, );
app = ();
httpServer = http.(app);
wsServer = ({
: httpServer,
:
});
() {
{
: ( (userIds) => {
users = db..({
: { : { : userIds } }
});
userMap = (users.( [user., user]));
userIds.( userMap.(id));
}),
: ( (postIds) => {
posts = db..({
: { : { : postIds } }
});
postMap = (posts.( [post., post]));
postIds.( postMap.(id));
}),
: ( (postIds) => {
comments = db..({
: { : { : postIds } },
: { : }
});
commentsByPost = {};
comments.( {
(!commentsByPost[comment.]) {
commentsByPost[comment.] = [];
}
commentsByPost[comment.].(comment);
});
postIds.( commentsByPost[id] || []);
})
};
}
serverCleanup = (
{
schema,
: (ctx) => {
{
db,
pubsub,
: (ctx.)
};
}
},
wsServer
);
server = ({
schema,
: [
({ httpServer }),
{
() {
{
() {
serverCleanup.();
}
};
}
}
],
: [
(),
({
: ,
: ,
: ,
: ,
:
})
],
: {
.(error);
(process.. === ) {
error..;
}
{
: error.,
: {
: error..,
: .()
}
};
}
});
() {
server.();
app.(
,
(),
express.(),
(server, {
: ({ req }) => {
token = req..?.(, );
user = token ? (token) : ;
loaders = (db);
{
db,
user,
loaders,
pubsub,
req
};
}
})
);
( httpServer.({ : }, resolve));
.();
}
().(.);
// resolvers/index.js
const { GraphQLScalarType } = require('graphql');
const { PubSub, withFilter } = require('graphql-subscriptions');
const DataLoader = require('dataloader');
const pubsub = new PubSub();
const resolvers = {
// Custom scalar for DateTime
DateTime: new GraphQLScalarType({
name: 'DateTime',
serialize: (value) => value.toISOString(),
parseValue: (value) => new Date(value),
parseLiteral: (ast) => new Date(ast.value)
}),
Query: {
me: async (_, __, { user, loaders }) => {
if (!user) throw new Error('Not authenticated');
return loaders.userLoader.load(user.id);
},
user: async (_, { id }, { loaders }) => {
loaders..(id);
},
: (_, { first = , after, filter }, { db }) => {
cursor = after ? { : after } : ;
users = db..({
: first + ,
: cursor ? : ,
cursor,
: filter,
: { : }
});
hasNextPage = users. > first;
edges = users.(, first).( ({
: user,
: user.
}));
{
edges,
: {
hasNextPage,
: !!after,
: edges[]?.,
: edges[edges. - ]?.
},
: db..({ : filter })
};
},
: (_, { id }, { loaders }) => {
post = loaders..(id);
(post && !post.) {
canView = (post, user);
(!canView) ;
}
post;
},
: (_, { first = , after, orderBy = , filter }, { db }) => {
where = {};
(filter) {
(filter. !== ) {
where. = filter.;
}
(filter.) {
where. = filter.;
}
(filter.?.) {
where. = {
: {
: { : filter. }
}
};
}
(filter.) {
where. = [
{ : { : filter., : } },
{ : { : filter., : } }
];
}
}
orderByMap = {
: { : },
: { : },
: { : },
: { : },
: { : }
};
cursor = after ? { : after } : ;
posts = db..({
: first + ,
: cursor ? : ,
cursor,
where,
: orderByMap[orderBy],
: {
: {
: { : , : }
}
}
});
hasNextPage = posts. > first;
edges = posts.(, first).( ({
: post,
: post.
}));
{
edges,
: {
hasNextPage,
: !!after,
: edges[]?.,
: edges[edges. - ]?.
},
: db..({ where })
};
},
: (_, { query, first = }, { db }) => {
db..({
: {
: ,
: [
{ : { : query, : } },
{ : { : query, : } },
{ : { : { : { : query, : } } } }
]
},
: first,
: { : }
});
},
: (_, { limit = }, { db }) => {
oneDayAgo = (.() - * * * );
db..({
: {
: ,
: [
{ : { : } },
{ : { : { : { : oneDayAgo } } } }
]
},
: [
{ : },
{ : },
{ : }
],
: limit
});
}
},
: {
: (_, { input }, { db }) => {
existingUser = db..({
: { : input. }
});
(existingUser) {
();
}
hashedPassword = bcrypt.(input., );
user = db..({
: {
: input.,
: input.,
: hashedPassword,
: input. ? {
: input.
} :
},
: { : }
});
token = (user);
{ token, user };
},
: (_, { input }, { db, user, pubsub }) => {
(!user) ();
post = db..({
: {
...input,
: user.,
: input. ? {
: input..( ({
: { : (tag) },
: { : tag, : (tag) }
}))
} :
},
: {
: ,
:
}
});
pubsub.(, { : post });
post;
},
: (_, { id, input }, { db, user }) => {
(!user) ();
post = db..({
: { id }
});
(!post) ();
(post. !== user. && user. !== ) {
();
}
updatedPost = db..({
: { id },
: {
...input,
: input. ? {
: [],
: input..( ({
: { : (tag) },
: { : tag, : (tag) }
}))
} :
},
: {
: ,
:
}
});
pubsub.(, { : updatedPost });
updatedPost;
},
: (_, { postId }, { db, user }) => {
(!user) ();
existingLike = db..({
: {
: {
: user.,
postId
}
}
});
(existingLike) {
db..({
: { : existingLike. }
});
db..({
: { : postId },
: { : { : } },
: { : , : }
});
} {
db..({
: {
: user.,
postId
}
});
db..({
: { : postId },
: { : { : } },
: { : , : }
});
}
}
},
: {
: {
: (
pubsub.([]),
{
(variables.) {
payload.. === variables.;
}
;
}
)
},
: {
: (
pubsub.([]),
{
payload.. === variables.;
}
)
}
},
: {
: (user, { first = , after }, { db }) => {
cursor = after ? { : after } : ;
posts = db..({
: { : user. },
: first + ,
: cursor ? : ,
cursor,
: { : }
});
hasNextPage = posts. > first;
edges = posts.(, first).( ({
: post,
: post.
}));
{
edges,
: {
hasNextPage,
: !!after,
: edges[]?.,
: edges[edges. - ]?.
},
: db..({ : { : user. } })
};
},
: (user, _, { loaders }) => {
loaders..(user.);
},
: (user, _, { loaders }) => {
loaders..(user.);
}
},
: {
: (post, _, { loaders }) => {
loaders..(post.);
},
: (post, { first = , after }, { db }) => {
comments = loaders..(post.);
startIndex = after ? comments.( c. === after) + : ;
paginatedComments = comments.(startIndex, startIndex + first + );
hasNextPage = paginatedComments. > first;
edges = paginatedComments.(, first).( ({
: comment,
: comment.
}));
{
edges,
: {
hasNextPage,
: !!after,
: edges[]?.,
: edges[edges. - ]?.
},
: comments.
};
}
},
: {
: (comment, _, { loaders }) => {
loaders..(comment.);
},
: (comment, _, { loaders }) => {
loaders..(comment.);
}
}
};
. = resolvers;
// directives/auth.js
const { mapSchema, getDirective, MapperKind } = require('@graphql-tools/utils');
const { defaultFieldResolver } = require('graphql');
function authDirective(schema, directiveName) {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const authDirective = getDirective(schema, fieldConfig, directiveName)?.[0];
if (authDirective) {
const { requires } = authDirective;
const { resolve = defaultFieldResolver } = fieldConfig;
fieldConfig.resolve = async function (source, args, context, info) {
// Check if user is authenticated
if (!context.user) {
throw new Error('Not authenticated');
}
// Check role requirements
if (requires) {
const hasRole = checkUserRole(context.user, requires);
if (!hasRole) {
throw new Error(`Requires ${requires} role`);
}
}
(source, args, context, info);
};
fieldConfig;
}
}
});
}
{ } = ();
rateLimiters = ();
() {
(schema, {
[.]: {
rateLimitDirective = (schema, fieldConfig, directiveName)?.[];
(rateLimitDirective) {
{ max, } = rateLimitDirective;
{ resolve = defaultFieldResolver } = fieldConfig;
key = ;
(!rateLimiters.(key)) {
rateLimiters.(key, ({
: max,
: ()
}));
}
limiter = rateLimiters.(key);
fieldConfig. = () {
userId = context.?. || context..;
{
limiter.(userId);
} (e) {
();
}
(source, args, context, info);
};
fieldConfig;
}
}
});
}
= ();
{ createHash } = ();
{
() {
. = redis;
}
() {
cached = ..(key);
cached ? .(cached) : ;
}
() {
..(key, ttl, .(value));
}
() {
query = info.;
argsString = .(args);
().().();
}
() {
{ ttl = } = options;
(source, args, context, info) => {
(info.. !== ) {
(source, args, context, info);
}
cacheKey = .(info, args);
cached = .(cacheKey);
(cached) {
cached;
}
result = (source, args, context, info);
.(cacheKey, result, ttl);
result;
};
}
}
. = { authDirective, rateLimitDirective, };
| Error | Cause | Solution |
|---|---|---|
| "Query depth limit exceeded" | Query too deeply nested | Adjust depth limit or simplify query |
| "Query complexity too high" | Query too expensive | Optimize query or increase complexity budget |
| "N+1 query detected" | Missing DataLoader | Implement DataLoader for relationship |
| "Subscription connection failed" | WebSocket issues | Check WebSocket configuration |
| "Schema validation failed" | Invalid GraphQL schema | Fix schema syntax errors |
Server Options
introspection: Enable schema introspection (disable in production)playground: Enable GraphQL Playgroundcors: CORS configurationuploads: File upload supportsubscriptions: WebSocket configurationPerformance Options
depthLimit: Maximum query depth (default: 10)costAnalysis: Query cost limitsdataLoader: Batching configurationcaching: Response caching settingspersistedQueries: APQ supportDO:
DON'T:
/api-authentication-builder - Add auth to GraphQL/api-documentation-generator - Generate GraphQL docs/api-testing-framework - Test GraphQL APIs/graphql-federation - Implement microservices