用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill graphql命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
基于 SOC 职业分类
正在显示 SKILL.md
| name | graphql |
| description | GraphQL — schema, resolvers, N+1/DataLoader, pagination. Use when this capability is needed. |
| metadata | {"author":"Dev-Toolbelt"} |
graphql or @graphql-tools/* in package.json.graphql / .gql schema files in the repositoryapollo-server, graphql-yoga, strawberry-graphql, ariadne, gqlgen, async-graphql dependencyGRAPHQL_ENDPOINT env var or /graphql route in routing configApolloClient, urql, or graphql-request on the frontendDetect which approach the project uses before writing any schema or resolver:
| Signal | Approach |
|---|---|
.graphql / .gql files are the source of truth | Schema-first |
| Schema generated from code annotations / decorators | Code-first |
Do not mix approaches. Follow whichever the project already uses.
| Element | Convention | Example |
|---|---|---|
| Types | PascalCase | User, OrderItem |
| Fields | camelCase | createdAt, totalAmount |
| Queries | camelCase verb-noun | user(id), listOrders |
| Mutations | camelCase action-noun | createUser, updateOrder, deleteProduct |
| Subscriptions | camelCase event | orderStatusChanged, messageReceived |
| Enums | SCREAMING_SNAKE_CASE values | ORDER_STATUS_PENDING |
| Input types | [Action][Type]Input | CreateUserInput, UpdateOrderInput |
Never expose internal database column names directly — map them to clean field names in the schema.
# Good — explicit, typed arguments
type Query {
user(id: ID!): User
users(filter: UserFilterInput, page: Int, perPage: Int): UserConnection!
order(id: ID!): Order
}
# Bad — catch-all input loses discoverability
type Query {
users(input: JSON): [User]
}
!) only when the server guarantees a value — never lie about nullability! on list fields ([User!]!) when neither the list nor its elements can be nullWrap mutation arguments in a single input object for extensibility and forward-compatibility:
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateOrder(id: ID!, input: UpdateOrderInput!): UpdateOrderPayload!
}
input CreateUserInput {
email: String!
name: String!
role: UserRole!
}
# Payload: return the mutated entity + optional user-facing errors
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String # which field caused the error (null = global error)
message: String!
code String
Rules:
errors: [UserError!]! (always-present empty array) instead of nullable error — this is the Errors-as-Data patternEvery resolver that loads related data by ID must use a DataLoader (batching + caching per request):
// Without DataLoader — fires 1 query per user (N+1)
const resolver = {
Order: {
user: (order) => db.users.findById(order.userId), // ❌
},
}
// With DataLoader — batches into 1 query for all orders in the request
const userLoader = new DataLoader(async (ids: string[]) => {
const users = await db.users.findByIds(ids);
return ids.map(id => users.find(u => u.id === id) ?? null);
});
const resolver = {
Order: {
user: (order, _, ctx) => ctx.loaders.user.load(order.userId), // ✅
},
}
Rules:
userLoader, productLoader, etc.)Use Relay cursor-based pagination for collections that can grow large. Use offset (page / perPage) only for small, stable lists.
# Relay-style connection pattern
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
users(first: Int, after: String, last: Int, before: String): UserConnection!
}
Rules:
[User!]! without pagination args) — always paginatetotalCount is expensive on large tables; make it optional or computed lazilytype Subscription {
orderStatusChanged(orderId: ID!): OrderStatusEvent!
}
type OrderStatusEvent {
orderId: ID!
status: OrderStatus!
updatedAt: String!
}
Rules:
| Error type | Where it goes | Example |
|---|---|---|
| Business rule violation | errors field in payload | "Email already taken" |
| Auth failure | Top-level GraphQL error with extensions.code: UNAUTHENTICATED | Token expired |
| Forbidden | Top-level GraphQL error with extensions.code: FORBIDDEN | Missing permission |
| Input validation | errors field in payload | "Email format invalid" |
| Unexpected server error | Top-level GraphQL error with extensions.code: INTERNAL_SERVER_ERROR | DB connection lost |
Never expose stack traces or internal error details to clients in production.
// Include machine-readable codes in extensions for client handling
throw new GraphQLError("Not authenticated", {
extensions: { code: "UNAUTHENTICATED" },
});
/graphql endpoint, not just on individual resolverserrors: [UserError!]! patternSource: Dev-Toolbelt/dev-team-agents — distributed by TomeVault.