home / skills / dexploarer / claudius-skills / graphql-schema-generator

This skill generates robust GraphQL schemas with types, resolvers, and subscriptions to accelerate API development.

This is most likely a fork of the graphql-schema-generator skill from dexploarer
npx playbooks add skill dexploarer/claudius-skills --skill graphql-schema-generator

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
5.8 KB
---
name: graphql-schema-generator
description: Generates GraphQL schemas with type definitions, resolvers, queries, mutations, and subscriptions. Use when building GraphQL APIs.
---

# GraphQL Schema Generator Skill

Expert at creating GraphQL schemas with proper types, resolvers, and best practices.

## When to Activate

- "create GraphQL schema for [entity]"
- "generate GraphQL API"
- "build GraphQL types and resolvers"

## Complete GraphQL Structure

```typescript
// schema/user.schema.ts
import { gql } from 'apollo-server-express';

export const userTypeDefs = gql`
  type User {
    id: ID!
    email: String!
    name: String!
    role: UserRole!
    posts: [Post!]!
    createdAt: DateTime!
    updatedAt: DateTime!
  }

  enum UserRole {
    USER
    ADMIN
    MODERATOR
  }

  input CreateUserInput {
    email: String!
    name: String!
    password: String!
    role: UserRole = USER
  }

  input UpdateUserInput {
    email: String
    name: String
    password: String
    role: UserRole
  }

  type UserConnection {
    edges: [UserEdge!]!
    pageInfo: PageInfo!
    totalCount: Int!
  }

  type UserEdge {
    node: User!
    cursor: String!
  }

  type Query {
    user(id: ID!): User
    users(
      first: Int = 10
      after: String
      search: String
    ): UserConnection!
    me: User
  }

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

  type Subscription {
    userCreated: User!
    userUpdated(id: ID!): User!
  }
`;

// resolvers/user.resolvers.ts
import { UserInputError, AuthenticationError } from 'apollo-server-express';
import { UserService } from '../services/user.service';
import { pubsub } from '../pubsub';

const USER_CREATED = 'USER_CREATED';
const USER_UPDATED = 'USER_UPDATED';

export const userResolvers = {
  Query: {
    user: async (_parent, { id }, { services, user }) => {
      if (!user) {
        throw new AuthenticationError('Not authenticated');
      }

      return await services.user.findById(id);
    },

    users: async (_parent, { first, after, search }, { services, user }) => {
      if (!user) {
        throw new AuthenticationError('Not authenticated');
      }

      const result = await services.user.findAll({
        first,
        after,
        search,
      });

      return {
        edges: result.users.map(user => ({
          node: user,
          cursor: Buffer.from(user.id.toString()).toString('base64'),
        })),
        pageInfo: {
          hasNextPage: result.hasNextPage,
          endCursor: result.endCursor,
        },
        totalCount: result.totalCount,
      };
    },

    me: async (_parent, _args, { user }) => {
      if (!user) {
        throw new AuthenticationError('Not authenticated');
      }

      return user;
    },
  },

  Mutation: {
    createUser: async (_parent, { input }, { services }) => {
      const user = await services.user.create(input);

      // Publish subscription event
      pubsub.publish(USER_CREATED, { userCreated: user });

      return user;
    },

    updateUser: async (_parent, { id, input }, { services, user }) => {
      if (!user) {
        throw new AuthenticationError('Not authenticated');
      }

      if (user.id !== id && user.role !== 'ADMIN') {
        throw new AuthenticationError('Not authorized');
      }

      const updatedUser = await services.user.update(id, input);

      if (!updatedUser) {
        throw new UserInputError('User not found');
      }

      // Publish subscription event
      pubsub.publish(USER_UPDATED, {
        userUpdated: updatedUser,
        id,
      });

      return updatedUser;
    },

    deleteUser: async (_parent, { id }, { services, user }) => {
      if (!user || user.role !== 'ADMIN') {
        throw new AuthenticationError('Admin access required');
      }

      const success = await services.user.delete(id);

      if (!success) {
        throw new UserInputError('User not found');
      }

      return true;
    },
  },

  Subscription: {
    userCreated: {
      subscribe: () => pubsub.asyncIterator([USER_CREATED]),
    },

    userUpdated: {
      subscribe: withFilter(
        () => pubsub.asyncIterator([USER_UPDATED]),
        (payload, variables) => {
          return payload.id === variables.id;
        }
      ),
    },
  },

  User: {
    // Field resolver for nested data
    posts: async (parent, _args, { services }) => {
      return await services.post.findByAuthorId(parent.id);
    },
  },
};
```

## DataLoader Pattern

```typescript
// dataloaders/user.dataloader.ts
import DataLoader from 'dataloader';
import { UserService } from '../services/user.service';

export function createUserLoader(userService: UserService) {
  return new DataLoader(async (ids: readonly string[]) => {
    const users = await userService.findByIds([...ids]);

    const userMap = new Map(users.map(user => [user.id, user]));

    return ids.map(id => userMap.get(id) || null);
  });
}

// Use in context
export const createContext = ({ req }) => {
  const userService = new UserService();

  return {
    user: req.user,
    services: {
      user: userService,
    },
    loaders: {
      user: createUserLoader(userService),
    },
  };
};
```

## Best Practices

- Use clear, descriptive type names
- Implement pagination (Connection pattern)
- Add input validation
- Use enums for fixed values
- Implement authentication/authorization
- Use DataLoaders to prevent N+1 queries
- Add proper error handling
- Document schema with descriptions
- Version your API
- Use subscriptions for real-time data
- Implement field-level resolvers
- Cache responses when appropriate

## Output Checklist

- ✅ Type definitions created
- ✅ Resolvers implemented
- ✅ Queries/Mutations/Subscriptions
- ✅ DataLoaders setup
- ✅ Authentication added
- ✅ Error handling
- 📝 Usage examples

Overview

This skill generates complete GraphQL schemas including type definitions, queries, mutations, subscriptions, and resolvers. It scaffolds pagination, input types, enums, field resolvers, and DataLoader integration to prevent N+1 issues. It also includes authentication/authorization hooks and subscription publishing so you get production-ready API scaffolding quickly.

How this skill works

Given an entity description or a request to create a GraphQL API, the skill outputs typeDefs and resolver skeletons that follow best practices like the Connection pattern and field-level resolvers. It wires service calls, DataLoader creation, context composition, pub/sub events for subscriptions, and common error handling (authentication, authorization, input errors). The output is TypeScript-friendly and ready to plug into Apollo Server or similar GraphQL servers.

When to use it

  • When you need a complete GraphQL API surface for a domain entity (types, queries, mutations, subscriptions)
  • When you want consistent pagination (Connection) and cursor handling implemented
  • When you need resolvers that include auth checks, error handling, and service integration
  • When preventing N+1 database queries with DataLoader is a priority
  • When you need example subscription events published for real-time updates

Best practices

  • Use clear, descriptive type and input names and add schema descriptions
  • Implement Connection-style pagination and return edges/pageInfo/totalCount
  • Validate inputs and return UserInputError for client-side problems
  • Enforce authentication/authorization in resolvers and use role checks for admin operations
  • Create DataLoaders per request and attach them to the context to avoid N+1
  • Publish subscription events on relevant mutations and use withFilter for targeted subscriptions

Example use cases

  • Generate a User schema with createUser, updateUser, deleteUser, users query and me query
  • Scaffold Post, Comment and nested field resolvers with DataLoader for author lookups
  • Add real-time notifications using subscriptions for created/updated resources
  • Create pagination-enabled list endpoints with cursor-based cursors and total counts
  • Produce context creation code that injects services, authenticated user and loaders

FAQ

Does the generated code handle authentication?

Yes — resolvers include authentication checks and common role-based authorization hooks; you can adapt them to your auth implementation.

How does it prevent N+1 queries?

It creates DataLoader factories and suggests attaching loaders to the request context so resolvers batch and cache related entity fetches.