GraphQL schema design, resolvers, mutations, subscriptions, DataLoader, Prisma integration, N+1 prevention
How this skill is triggered — by the user, by Claude, or both
Slash command
/claudient-database:graphqlThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Designing a GraphQL schema (types, queries, mutations, subscriptions)
# Type names: PascalCase singular nouns
# Field names: camelCase
# Enums: SCREAMING_SNAKE_CASE values
type Order {
id: ID!
status: OrderStatus!
customer: Customer! # Nested type — always return the object, not just the ID
items: [OrderItem!]! # Non-null list of non-null items
totalAmount: Float!
createdAt: DateTime!
}
enum OrderStatus {
PENDING
COMPLETED
CANCELLED
}
# Queries: return nullable for single item (not found = null), non-null for lists
type Query {
order(id: ID!): Order # Nullable — null if not found
orders(filter: OrderFilter): [Order!]! # Non-null list
me: User # Nullable — null if not authenticated
}
# Mutations: always return the mutated object plus an errors array
type Mutation {
createOrder(input: CreateOrderInput!): CreateOrderPayload!
}
type CreateOrderPayload {
order: Order # Null if mutation failed
errors: [UserError!]! # Empty if successful
}
type UserError {
field: String
message: String!
}
import DataLoader from 'dataloader';
// Create per-request — never singleton (request data isolation)
export function createLoaders() {
return {
customerLoader: new DataLoader<string, Customer>(async (ids) => {
const customers = await db.customer.findMany({
where: { id: { in: [...ids] } }
});
// Must return in same order as ids
const customerMap = new Map(customers.map(c => [c.id, c]));
return ids.map(id => customerMap.get(id) ?? new Error(`Customer ${id} not found`));
}),
};
}
// Resolver — uses loader, not direct DB call
const resolvers = {
Order: {
customer: (order, _, { loaders }) => loaders.customerLoader.load(order.customerId),
}
};
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
orders(first: Int, after: String, last: Int, before: String): OrderConnection!
}
// Field-level authorization in resolver
const resolvers = {
Query: {
adminStats: (_, __, { user }) => {
if (!user || user.role !== 'ADMIN') {
throw new GraphQLError('Unauthorized', {
extensions: { code: 'UNAUTHORIZED' }
});
}
return getAdminStats();
}
},
Order: {
// Object-level: only return sensitive fields to the order's owner
internalNotes: (order, _, { user }) => {
if (user?.id !== order.customerId && user?.role !== 'ADMIN') return null;
return order.internalNotes;
}
}
};
// Resolver using Prisma — avoid over-fetching
const resolvers = {
Query: {
order: async (_, { id }, { prisma, user }) => {
const order = await prisma.order.findUnique({
where: { id },
select: {
id: true,
status: true,
customerId: true,
totalAmount: true,
createdAt: true,
// Do NOT select items here — let the items resolver handle it
// with DataLoader to avoid N+1
}
});
if (!order) return null;
if (order.customerId !== user?.id) throw new GraphQLError('Forbidden');
return order;
}
}
};
User: Design a GraphQL schema and resolvers for a simple e-commerce API — products, orders, and customers. Include pagination, DataLoader for customers, and mutation error handling.
Expected output:
Product, Order, Customer, OrderConnection, UserError typesQuery.orders with cursor pagination returning OrderConnectionMutation.createOrder returning CreateOrderPayload with errors arrayOrder.customer resolver using DataLoader (not direct DB query)createLoaders() function per request, batching customer lookups by IDWork with us: Claudient is backed by Uitbreiden — we build AI products and B2B solutions with developer communities. Building GraphQL APIs or AI-powered data layers? uitbreiden.com · Reddit · YouTube
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.
npx claudepluginhub claudient/claudient --plugin claudient-database