Claude Code Plugins

Community-maintained marketplace

Feedback

GraphQL API development including schema design, resolvers, queries, mutations, subscriptions, and integration with Node.js, Apollo, and other frameworks. Use when building GraphQL APIs, designing GraphQL schemas, implementing resolvers, or debugging GraphQL issues.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name graphql
description GraphQL API development including schema design, resolvers, queries, mutations, subscriptions, and integration with Node.js, Apollo, and other frameworks. Use when building GraphQL APIs, designing GraphQL schemas, implementing resolvers, or debugging GraphQL issues.

GraphQL Development

GraphQL API design, implementation, and best practices.

Schema Design

Type Definitions

# Scalar types
type User {
    id: ID!
    email: String!
    name: String
    age: Int
    balance: Float
    isActive: Boolean!
    createdAt: DateTime!  # Custom scalar
}

# Enum types
enum UserRole {
    ADMIN
    EDITOR
    USER
}

enum OrderStatus {
    PENDING
    PROCESSING
    SHIPPED
    DELIVERED
    CANCELLED
}

# Input types (for mutations)
input CreateUserInput {
    email: String!
    name: String!
    password: String!
    role: UserRole = USER
}

input UpdateUserInput {
    name: String
    email: String
}

# Interface
interface Node {
    id: ID!
}

type User implements Node {
    id: ID!
    email: String!
}

# Union types
union SearchResult = User | Post | Comment

Relationships

type User {
    id: ID!
    email: String!
    posts: [Post!]!                    # One-to-many
    profile: Profile                   # One-to-one (nullable)
    followers: [User!]!                # Self-referential
    following: [User!]!
}

type Post {
    id: ID!
    title: String!
    content: String!
    author: User!                      # Many-to-one
    tags: [Tag!]!                      # Many-to-many
    comments(first: Int, after: String): CommentConnection!
}

# Connection pattern for pagination
type CommentConnection {
    edges: [CommentEdge!]!
    pageInfo: PageInfo!
    totalCount: Int!
}

type CommentEdge {
    cursor: String!
    node: Comment!
}

type PageInfo {
    hasNextPage: Boolean!
    hasPreviousPage: Boolean!
    startCursor: String
    endCursor: String
}

Queries & Mutations

Query Types

type Query {
    # Single item
    user(id: ID!): User
    userByEmail(email: String!): User

    # Lists with filtering
    users(
        filter: UserFilter
        orderBy: UserOrderBy
        first: Int
        after: String
    ): UserConnection!

    # Search
    search(query: String!, types: [SearchType!]): [SearchResult!]!

    # Current user
    me: User
}

input UserFilter {
    role: UserRole
    isActive: Boolean
    createdAfter: DateTime
}

input UserOrderBy {
    field: UserSortField!
    direction: SortDirection!
}

enum UserSortField {
    CREATED_AT
    NAME
    EMAIL
}

enum SortDirection {
    ASC
    DESC
}

Mutation Types

type Mutation {
    # Create
    createUser(input: CreateUserInput!): CreateUserPayload!

    # Update
    updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!

    # Delete
    deleteUser(id: ID!): DeleteUserPayload!

    # Authentication
    login(email: String!, password: String!): AuthPayload!
    logout: Boolean!
    refreshToken(token: String!): AuthPayload!
}

# Payload pattern (recommended)
type CreateUserPayload {
    user: User
    errors: [Error!]
}

type Error {
    field: String
    message: String!
    code: ErrorCode!
}

enum ErrorCode {
    VALIDATION_ERROR
    NOT_FOUND
    UNAUTHORIZED
    FORBIDDEN
}

Subscriptions

type Subscription {
    # Real-time updates
    postCreated: Post!
    commentAdded(postId: ID!): Comment!
    userStatusChanged(userId: ID!): User!

    # With filtering
    messageReceived(roomId: ID!): Message!
}

Apollo Server (Node.js)

Setup

import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import express from 'express';

const typeDefs = `#graphql
    type Query {
        users: [User!]!
        user(id: ID!): User
    }

    type User {
        id: ID!
        email: String!
        posts: [Post!]!
    }
`;

const resolvers = {
    Query: {
        users: async (_, __, { dataSources }) => {
            return dataSources.userAPI.getUsers();
        },
        user: async (_, { id }, { dataSources }) => {
            return dataSources.userAPI.getUser(id);
        },
    },
    User: {
        posts: async (parent, _, { dataSources }) => {
            return dataSources.postAPI.getPostsByAuthor(parent.id);
        },
    },
};

const server = new ApolloServer({
    typeDefs,
    resolvers,
});

const app = express();
await server.start();

app.use(
    '/graphql',
    express.json(),
    expressMiddleware(server, {
        context: async ({ req }) => ({
            token: req.headers.authorization,
            dataSources: {
                userAPI: new UserAPI(),
                postAPI: new PostAPI(),
            },
        }),
    })
);

Resolvers

const resolvers = {
    Query: {
        // Arguments: parent, args, context, info
        user: async (_, { id }, { dataSources, user }) => {
            return dataSources.userAPI.getUser(id);
        },

        users: async (_, { filter, first, after }, { dataSources }) => {
            const users = await dataSources.userAPI.getUsers({
                filter,
                limit: first,
                cursor: after,
            });
            return formatConnection(users);
        },
    },

    Mutation: {
        createUser: async (_, { input }, { dataSources }) => {
            try {
                const user = await dataSources.userAPI.create(input);
                return { user, errors: null };
            } catch (error) {
                return {
                    user: null,
                    errors: [{ message: error.message, code: 'VALIDATION_ERROR' }],
                };
            }
        },
    },

    // Field-level resolvers
    User: {
        fullName: (parent) => `${parent.firstName} ${parent.lastName}`,
        posts: async (parent, { first }, { dataSources }) => {
            return dataSources.postAPI.getByAuthor(parent.id, { limit: first });
        },
    },

    // Custom scalars
    DateTime: new GraphQLScalarType({
        name: 'DateTime',
        parseValue: (value) => new Date(value),
        serialize: (value) => value.toISOString(),
    }),
};

DataLoader (N+1 Prevention)

import DataLoader from 'dataloader';

// Create loader
const userLoader = new DataLoader(async (userIds) => {
    const users = await db.users.findMany({
        where: { id: { in: userIds } },
    });
    // Return in same order as input
    return userIds.map((id) => users.find((u) => u.id === id));
});

// In resolver
const resolvers = {
    Post: {
        author: (parent, _, { loaders }) => {
            return loaders.userLoader.load(parent.authorId);
        },
    },
};

// Context setup
const context = ({ req }) => ({
    loaders: {
        userLoader: new DataLoader(batchUsers),
    },
});

Authentication & Authorization

Context-based Auth

const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: async ({ req }) => {
        const token = req.headers.authorization?.replace('Bearer ', '');
        let user = null;

        if (token) {
            try {
                user = await verifyToken(token);
            } catch (e) {
                // Invalid token, user stays null
            }
        }

        return { user };
    },
});

// In resolver
const resolvers = {
    Query: {
        me: (_, __, { user }) => {
            if (!user) throw new AuthenticationError('Not authenticated');
            return user;
        },
    },
};

Directive-based Auth

directive @auth(requires: Role = USER) on FIELD_DEFINITION

type Query {
    publicPosts: [Post!]!
    myPosts: [Post!]! @auth
    allUsers: [User!]! @auth(requires: ADMIN)
}
import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils';

function authDirective(directiveName) {
    return {
        authDirectiveTransformer: (schema) =>
            mapSchema(schema, {
                [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
                    const directive = getDirective(schema, fieldConfig, directiveName)?.[0];
                    if (directive) {
                        const { resolve = defaultFieldResolver } = fieldConfig;
                        fieldConfig.resolve = async function (source, args, context, info) {
                            if (!context.user) {
                                throw new AuthenticationError('Not authenticated');
                            }
                            const requiredRole = directive.requires;
                            if (requiredRole && context.user.role !== requiredRole) {
                                throw new ForbiddenError('Not authorized');
                            }
                            return resolve(source, args, context, info);
                        };
                    }
                    return fieldConfig;
                },
            }),
    };
}

Error Handling

import { GraphQLError } from 'graphql';

// Custom errors
class NotFoundError extends GraphQLError {
    constructor(message) {
        super(message, {
            extensions: {
                code: 'NOT_FOUND',
                http: { status: 404 },
            },
        });
    }
}

class ValidationError extends GraphQLError {
    constructor(errors) {
        super('Validation failed', {
            extensions: {
                code: 'VALIDATION_ERROR',
                validationErrors: errors,
                http: { status: 400 },
            },
        });
    }
}

// Usage in resolver
const resolvers = {
    Query: {
        user: async (_, { id }) => {
            const user = await db.users.findUnique({ where: { id } });
            if (!user) {
                throw new NotFoundError(`User ${id} not found`);
            }
            return user;
        },
    },
};

Performance

Query Complexity

import { createComplexityLimitRule } from 'graphql-validation-complexity';

const server = new ApolloServer({
    typeDefs,
    resolvers,
    validationRules: [
        createComplexityLimitRule(1000, {
            scalarCost: 1,
            objectCost: 10,
            listFactor: 20,
        }),
    ],
});

Depth Limiting

import depthLimit from 'graphql-depth-limit';

const server = new ApolloServer({
    typeDefs,
    resolvers,
    validationRules: [depthLimit(10)],
});

Caching

type Query {
    user(id: ID!): User @cacheControl(maxAge: 60)
    posts: [Post!]! @cacheControl(maxAge: 30, scope: PUBLIC)
}

type User @cacheControl(maxAge: 120) {
    id: ID!
    email: String! @cacheControl(maxAge: 0, scope: PRIVATE)
}

Testing

import { ApolloServer } from '@apollo/server';

describe('User Queries', () => {
    let server;

    beforeAll(() => {
        server = new ApolloServer({
            typeDefs,
            resolvers,
        });
    });

    it('should return user by id', async () => {
        const response = await server.executeOperation({
            query: `
                query GetUser($id: ID!) {
                    user(id: $id) {
                        id
                        email
                    }
                }
            `,
            variables: { id: '1' },
        });

        expect(response.body.singleResult.errors).toBeUndefined();
        expect(response.body.singleResult.data?.user).toEqual({
            id: '1',
            email: 'test@example.com',
        });
    });
});