manutej/luxor-claude-marketplace3 files

GraphQL API Development

Comprehensive guide for building GraphQL APIs including schema design, queries, mutations, subscriptions, resolvers, type system, error handling, authentication, authorization, caching strategies, and production best practices

Specification
Skill ID
manutej/luxor-claude-marketplace/graphql-api-development
Publisher
manutej
Repository
luxor-claude-marketplace
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

manutej/luxor-claude-marketplace/graphql-api-developmentInstalls these files
  • EXAMPLES.md
  • README.md
  • SKILL.md

What this skill tells the agent

GraphQL API Development

A comprehensive skill for building production-ready GraphQL APIs using graphql-js. Master schema design, type systems, resolvers, queries, mutations, subscriptions, authentication, authorization, caching, testing, and deployment strategies.

When to Use This Skill

Use this skill when:

  • Building a new API that requires flexible data fetching for web or mobile clients
  • Replacing or augmenting REST APIs with more efficient data access patterns
  • Developing APIs for applications with complex, nested data relationships
  • Creating APIs that serve multiple client types (web, mobile, desktop) with different data needs
  • Building real-time applications requiring subscriptions and live updates
  • Designing APIs where clients need to specify exactly what data they need
  • Developing GraphQL servers with Node.js and Express
  • Implementing type-safe APIs with strong schema validation
  • Creating self-documenting APIs with built-in introspection
  • Building microservices that need to be composed into a unified API

When GraphQL Excels Over REST

GraphQL Advantages

  1. Precise Data Fetching: Clients request exactly what they need, no over/under-fetching
  2. Single Request: Fetch multiple resources in one roundtrip instead of multiple REST endpoints
  3. Strongly Typed: Schema defines exact types, enabling validation and tooling
  4. Introspection: Self-documenting API with queryable schema
  5. Versioning Not Required: Add new fields without breaking existing queries
  6. Real-time Updates: Built-in subscription support for live data
  7. Nested Resources: Naturally handle complex relationships without N+1 queries
  8. Client-Driven: Clients control data shape, reducing backend changes

When to Stick with REST

  • Simple CRUD operations with standard resources
  • File uploads/downloads (GraphQL requires multipart handling)
  • HTTP caching is critical (GraphQL typically uses POST)
  • Team unfamiliar with GraphQL (learning curve)
  • Existing REST infrastructure works well

Core Concepts

The GraphQL Type System

GraphQL's type system is its foundation. Every GraphQL API defines:

  1. Scalar Types: Basic data types (String, Int, Float, Boolean, ID)
  2. Object Types: Complex types with fields
  3. Query Type: Entry point for read operations
  4. Mutation Type: Entry point for write operations
  5. Subscription Type: Entry point for real-time updates
  6. Input Types: Complex inputs for mutations
  7. Enums: Fixed set of values
  8. Interfaces: Abstract types that objects implement
  9. Unions: Types that can be one of several types
  10. Non-Null Types: Types that cannot be null
  11. List Types: Arrays of types

Schema Definition

Two approaches for defining GraphQL schemas:

1. Schema Definition Language (SDL) - Declarative, readable:

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

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

type Query {
  user(id: ID!): User
  posts: [Post!]!
}

2. Programmatic API - Type-safe, programmatic:

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: {
    id: { type: new GraphQLNonNull(GraphQLID) },
    name: { type: new GraphQLNonNull(GraphQLString) },
    email: { type: new GraphQLNonNull(GraphQLString) },
    posts: { type: new GraphQLList(new GraphQLNonNull(PostType)) }
  }
});

Resolvers

Resolvers are functions that return data for schema fields. Every field can have a resolver:

const resolvers = {
  Query: {
    user: (parent, args, context, info) => {
      return context.db.findUserById(args.id);
    }
  },
  User: {
    posts: (user, args, context) => {
      return context.db.findPostsByAuthorId(user.id);
    }
  }
};

Resolver Function Signature:

  • parent: The result from the parent resolver
  • args: Arguments passed to the field
  • context: Shared context (database, auth, etc.)
  • info: Field-specific metadata

Queries

Queries fetch data from your API:

query GetUser {
  user(id: "123") {
    id
    name
    email
    posts {
      title
      content
    }
  }
}

Mutations

Mutations modify data:

mutation CreatePost {
  createPost(input: {
    title: "GraphQL is awesome"
    content: "Here's why..."
    authorId: "123"
  }) {
    id
    title
    author {
      name
    }
  }
}

Subscriptions

Subscriptions enable real-time updates:

subscription OnPostCreated {
  postCreated {
    id
    title
    author {
      name
    }
  }
}

Schema Design Patterns

Pattern 1: Input Types for Mutations

Always use input types for complex mutation arguments:

input CreateUserInput {
  name: String!
  email: String!
  age: Int
  bio: String
}

type Mutation {
  createUser(input: CreateUserInput!): User!
}

Why: Easier to extend, better organization, reusable across mutations.

Pattern 2: Interfaces for Shared Fields

Use interfaces when multiple types share fields:

interface Node {
  id: ID!
  createdAt: String!
  updatedAt: String!
}

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

type Post implements Node {
  id: ID!
  createdAt: String!
  updatedAt: String!
  title: String!
  content: String
}

Pattern 3: Unions for Polymorphic Returns

Use unions when a field can return different types:

union SearchResult = User | Post | Comment

type Query {
  search(query: String!): [SearchResult!]!
}

Pattern 4: Pagination Patterns

Offset-based pagination:

type Query {
  posts(offset: Int, limit: Int): PostConnection!
}

type PostConnection {
  items: [Post!]!
  total: Int!
  hasMore: Boolean!
}

Cursor-based pagination (Relay-style):

type Query {
  posts(first: Int, after: String): PostConnection!
}

type PostConnection {
  edges: [PostEdge!]!
  pageInfo: PageInfo!
}

type PostEdge {
  node: Post!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  endCursor: String
}

Pattern 5: Error Handling

Field-level errors:

type MutationPayload {
  success: Boolean!
  message: String
  user: User
  errors: [Error!]
}

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

Union-based error handling:

union CreateUserResult = User | ValidationError | DatabaseError

type ValidationError {
  field: String!
  message: String!
}

Pattern 6: Versioning with Directives

Deprecate fields instead of versioning:

type User {
  name: String! @deprecated(reason: "Use firstName and lastName")
  firstName: String!
  lastName: String!
}

Query Optimization and Performance

The N+1 Problem

Problem: Fetching nested data causes multiple database queries:

// BAD: N+1 queries
const UserType = new GraphQLObjectType({
  name: 'User',
  fields: {
    posts: {
      type: new GraphQLList(PostType),
      resolve: (user) => {
        // This runs once PER user!
        return db.getPostsByUserId(user.id);
      }
    }
  }
});

// Query for 100 users = 1 query for users + 100 queries for posts = 101 queries

DataLoader Solution

DataLoader batches and caches requests:

import DataLoader from 'dataloader';

// Create DataLoader
const postLoader = new DataLoader(async (userIds) => {
  // Single query for all user IDs
  const posts = await db.getPostsByUserIds(userIds);

  // Group posts by userId
  const postsByUserId = {};
  posts.forEach(post => {
    if (!postsByUserId[post.authorId]) {
      postsByUserId[post.authorId] = [];
    }
    postsByUserId[post.authorId].push(post);
  });

  // Return in same order as userIds
  return userIds.map(id => postsByUserId[id] || []);
});

// Use in resolver
const UserType = new GraphQLObjectType({
  name: 'User',
  fields: {
    posts: {

Use this skill in a project

Open any RiverX project, search the Skills panel for manutej/luxor-claude-marketplace/graphql-api-development, and install it in one click.

Use this skill