home / skills / dexploarer / hyper-forge / graphql-schema-generator

graphql-schema-generator skill

/.claude/skills/graphql-schema-generator

This skill generates GraphQL schemas with types, resolvers, and subscriptions for robust API design.

npx playbooks add skill dexploarer/hyper-forge --skill graphql-schema-generator

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

Files (1)
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 with type definitions, resolvers, queries, mutations, subscriptions, and DataLoader integration. It produces TypeScript-ready code that follows pagination, authentication, and error-handling best practices. Use it to scaffold production-grade GraphQL APIs quickly and consistently.

How this skill works

I inspect the requested domain entities and produce typeDefs (types, inputs, enums, connection patterns) and resolver stubs for Query, Mutation, Subscription, and field-level resolvers. I include DataLoader factories and a context bootstrap for per-request loaders, plus example pub/sub wiring for realtime subscriptions. I also add auth checks, input validation hints, and common error patterns in resolvers.

When to use it

  • Scaffold a new GraphQL API for an entity or service
  • Add types, pagination, and subscriptions to an existing project
  • Prevent N+1 database issues with DataLoader integration
  • Enforce consistent authentication and authorization patterns
  • Generate TypeScript-compatible schema and resolver templates

Best practices

  • Use descriptive type and input names to make the schema self-documenting
  • Implement the Connection pattern for pagination with edges and pageInfo
  • Add input validation and explicit error handling (UserInputError, AuthenticationError)
  • Integrate DataLoaders in the request context to avoid N+1 queries
  • Use enums for fixed sets and document fields with descriptions
  • Publish subscription events via a centralized pubsub and filter with withFilter

Example use cases

  • Create a full User schema with create/update/delete mutations, queries, and real-time events
  • Generate schema and resolvers for Post and Comment with author field resolvers using DataLoader
  • Add an admin-only mutation set that enforces role checks in resolvers
  • Bootstrap paging-enabled list queries for resources with cursor-based pagination
  • Add subscriptions to notify clients when entities are created or updated

FAQ

Does the generated schema include authentication and authorization?

Yes. Resolver templates include authentication checks and role-based authorization examples (AuthenticationError). You can extend them to match your auth system.

How does DataLoader integration work in the generated code?

A per-request context factory creates DataLoaders (e.g., createUserLoader) and attaches them to context.loaders so field resolvers can batch and cache related queries to prevent N+1 problems.