AI Skill Library

GraphQL Essentials

Schema, queries, mutations, subscriptions, resolvers, N+1 problem.

graphqlapibackend
# GraphQL Essentials

## Schema (SDL)
```graphql
type User {
  id: ID!
  name: String!
  posts: [Post!]!
}
type Post {
  id: ID!
  title: String!
  author: User!
}
type Query {
  user(id: ID!): User
  users: [User!]!
}
type Mutation {
  createPost(title: String!, authorId: ID!): Post!
}
```

## Query
```graphql
query GetUser($id: ID!) {
  user(id: $id) { name posts { title } }
}
```

## Resolvers (Node.js)
```ts
const resolvers = {
  Query: { user: (_, { id }, ctx) => ctx.db.users.findById(id) },
  User: { posts: (parent, _, ctx) => ctx.db.posts.findByAuthor(parent.id) },
  Mutation: { createPost: (_, args, ctx) => ctx.db.posts.create(args) },
}
```

## N+1 & DataLoader
Each field resolver triggers a separate DB query. Fix with DataLoader:
```ts
const loader = new DataLoader(ids => db.posts.findByAuthors(ids))
User: { posts: (p) => loader.load(p.id) }
```

## Pagination (Relay cursor spec)
```graphql
users(first: 10, after: "cursor") {
  edges { node { id name } cursor }
  pageInfo { hasNextPage endCursor }
}
```

## Errors
GraphQL always returns HTTP 200. Errors live in `errors[]`. Use `extensions.code` for machine-readable codes.

## Fragments
```graphql
fragment UserFields on User { id name }
query { a: user(id:"1") { ...UserFields } b: user(id:"2") { ...UserFields } }
```

API: /api/skills/graphql-essentials