Aller au contenu principal

Skill: dev-graphql

Inline

GraphQL API development. Trigger when the user wants to create schemas, resolvers, or GraphQL queries.

Configuration​

PropertyValue
Contextinline
Pre-approved toolsNone declared: the project's permission settings apply
Triggerkeywords: dev, graphql

Detailed description​

GraphQL Development

Schema Definition​

type User {
id: ID!
email: String!
name: String!
posts: [Post!]!
createdAt: DateTime!
}

type Post {
id: ID!
title: String!
content: String
author: User!
published: Boolean!
}

type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
}

type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
}

input CreateUserInput {
email: String!
name: String!
password: String!
}

Resolvers​

const resolvers = {
Query: {
user: (_, { id }, ctx) => ctx.userService.findById(id),
users: (_, { limit, offset }, ctx) => ctx.userService.findAll({ limit, offset }),
},

Mutation: {
createUser: (_, { input }, ctx) => ctx.userService.create(input),
},

User: {
posts: (user, _, ctx) => ctx.postService.findByAuthor(user.id),
},
};

DataLoader (N+1 Prevention)​

import DataLoader from 'dataloader';

const userLoader = new DataLoader(async (ids: string[]) => {
const users = await userService.findByIds(ids);
return ids.map(id => users.find(u => u.id === id));
});

// In the context
context: ({ req }) => ({
userLoader,
postLoader: createPostLoader(),
});

Client (Apollo)​

const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
id
title
}
}
}
`;

function UserProfile({ userId }) {
const { data, loading, error } = useQuery(GET_USER, {
variables: { id: userId }
});

if (loading) return <Loading />;
if (error) return <Error message={error.message} />;

return <Profile user={data.user} />;
}

See also​

Apollo GraphQL publishes their own official agent skills at apollographql/skills (maintained by the Apollo team, last commit 2026-05-04). The repo covers Apollo Client, Apollo Server 5, Apollo Connectors, Federation 2, and Apollo Kotlin — the Apollo-specific stack.

When working on a project that uses Apollo, install the vendor skill alongside this one. This skill captures the stack-agnostic angle (schema design, DataLoader, N+1 prevention, query depth limits, generic security) that complements Apollo's vendor-specific guidance. Both together is the recommended setup for Apollo users; for non-Apollo GraphQL stacks (Yoga, Pothos, Mercurius, Strawberry, gqlgen), this skill remains the primary reference.

Install command and full list of validated vendor skills: docs/recipes/recommended-vendor-skills.md. Audit pilot trace: specs/marketplace-audit/dev-skills-pilot-2026-05-05.md.

Automatic triggering​

This skill is automatically activated when:

  • The matching keywords are detected in the conversation
  • The task context matches the skill's domain

Triggering examples​

  • "I want to dev..."
  • "I want to graphql..."

Context inline​

Inline means the skill runs in the conversation (the default, no context key):

  • Sees the full history
  • Its work is visible as it happens
  • Right for almost every skill

Practical examples​

1. Example: GraphQL Schema + Resolvers + Queries​

Example: GraphQL Schema + Resolvers + Queries

Scenario​

A bookstore API with authors and books, including pagination and nested queries.

Schema Definition​

# schema.graphql
type Query {
book(id: ID!): Book
books(first: Int = 10, after: String): BookConnection!
author(id: ID!): Author
}

type Mutation {
createBook(input: CreateBookInput!): Book!
}

type Book {
id: ID!
title: String!
isbn: String!
author: Author!
publishedAt: DateTime
}

type Author {
id: ID!
name: String!
books: [Book!]!
}

input CreateBookInput {
title: String!
isbn: String!
authorId: ID!
}

type BookConnection {
edges: [BookEdge!]!
pageInfo: PageInfo!
}

type BookEdge {
node: Book!
cursor: String!
}

type PageInfo {
hasNextPage: Boolean!
endCursor: String
}

Resolvers​

// resolvers/book.resolver.ts
export const bookResolvers = {
Query: {
book: async (_, { id }, { dataSources }) => {
return dataSources.bookAPI.getById(id);
},
books: async (_, { first, after }, { dataSources }) => {
return dataSources.bookAPI.getPaginated(first, after);
},
},

Mutation: {
createBook: async (_, { input }, { dataSources, user }) => {
if (!user) throw new AuthenticationError('Login required');
return dataSources.bookAPI.create(input);
},
},

// Nested resolver: avoids N+1 with DataLoader
Book: {
author: async (book, _, { loaders }) => {
return loaders.authorLoader.load(book.authorId);
},
},

Author: {
books: async (author, _, { dataSources }) => {
return dataSources.bookAPI.getByAuthorId(author.id);
},
},
};

Client Query​

# Fetch books with authors (single request, no over-fetching)
query GetBooks($first: Int!, $after: String) {
books(first: $first, after: $after) {
edges {
node {
id
title
author {
name
}
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}

Key Decisions​

  • Relay-style pagination: Connection/Edge/PageInfo pattern for cursor-based pagination
  • DataLoader for N+1: authorLoader.load() batches author lookups into a single query
  • Input types for mutations: Separate CreateBookInput keeps mutations clean and versionable
  • Auth in context: User injected via context, checked in mutation resolvers
  • Nested resolvers: Book.author resolved lazily, only when client requests it

See also​