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
- Skill ID
- manutej/luxor-claude-marketplace/graphql-api-development
- Publisher
- manutej
- Repository
- luxor-claude-marketplace
- Installs
- 0
- Files
- 3
- Synced
- Sep 23, 2026
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
- Precise Data Fetching: Clients request exactly what they need, no over/under-fetching
- Single Request: Fetch multiple resources in one roundtrip instead of multiple REST endpoints
- Strongly Typed: Schema defines exact types, enabling validation and tooling
- Introspection: Self-documenting API with queryable schema
- Versioning Not Required: Add new fields without breaking existing queries
- Real-time Updates: Built-in subscription support for live data
- Nested Resources: Naturally handle complex relationships without N+1 queries
- 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:
- Scalar Types: Basic data types (String, Int, Float, Boolean, ID)
- Object Types: Complex types with fields
- Query Type: Entry point for read operations
- Mutation Type: Entry point for write operations
- Subscription Type: Entry point for real-time updates
- Input Types: Complex inputs for mutations
- Enums: Fixed set of values
- Interfaces: Abstract types that objects implement
- Unions: Types that can be one of several types
- Non-Null Types: Types that cannot be null
- 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 resolverargs: Arguments passed to the fieldcontext: 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 queriesDataLoader 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: {More from manutej
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.
