All skills
apollographql avatar

/graphql-operations

@194056e official
by Apollo GraphQLapollographql/skills115 stars
13

Guide for writing GraphQL operations (queries, mutations, fragments) following best practices. Use this skill when: (1) writing GraphQL queries or mutations, (2) organizing operations with fragments, (3) optimizing data fetching patterns, (4) setting up type generation or linting, (5) reviewing operations for efficiency.

Use this Skill: https://skilld.dev/gh/apollographql/skills/graphql-operations

This session only. Nothing lands on disk.

referencesmutations.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Mutation Patterns

This reference covers patterns for writing effective GraphQL mutations.

Table of Contents

Mutation Structure

Basic Mutation

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    createdAt
  }
}

Variables:

{
  "input": {
    "title": "My Post",
    "content": "Post content..."
  }
}

Mutation with Multiple Arguments

mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
  updatePost(id: $id, input: $input) {
    id
    title
    updatedAt
  }
}

Multiple Mutations

Execute multiple mutations in one request (sequential execution):

mutation SetupUserProfile($userId: ID!, $profileInput: ProfileInput!, $settingsInput: SettingsInput!) {
  updateProfile(userId: $userId, input: $profileInput) {
    id
    bio
  }
  updateSettings(userId: $userId, input: $settingsInput) {
    id
    theme
    notifications
  }
}

Input Patterns

Single Input Object

Recommended pattern - single input argument:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    email
  }
}
{
  "input": {
    "email": "user@example.com",
    "name": "John Doe",
    "password": "secret123"
  }
}

Nested Input Objects

mutation CreateOrder($input: CreateOrderInput!) {
  createOrder(input: $input) {
    id
    total
  }
}
{
  "input": {
    "items": [
      { "productId": "prod_1", "quantity": 2 },
      { "productId": "prod_2", "quantity": 1 }
    ],
    "shippingAddress": {
      "street": "123 Main St",
      "city": "New York",
      "country": "US"
    }
  }
}

Optional Fields

mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
    bio
  }
}
{
  "id": "user_123",
  "input": {
    "name": "New Name"
    // bio not included - won't be changed
  }
}

Response Selection

Return the Modified Object

Always return the mutated object with updated fields:

mutation UpdatePost($id: ID!, $input: UpdatePostInput!) {
  updatePost(id: $id, input: $input) {
    id
    title
    content
    updatedAt # Server-set field
  }
}

Return Related Objects

If mutation affects related data, include it:

mutation AddComment($input: AddCommentInput!) {
  addComment(input: $input) {
    id
    body
    post {
      id
      commentCount # Updated count
    }
    author {
      id
      name
    }
  }
}

Return for Cache Updates

Select fields needed to update your cache:

mutation DeletePost($id: ID!) {
  deletePost(id: $id) {
    id # Needed to remove from cache
    author {
      id
      postCount # May need to decrement
    }
  }
}

Return Connections for List Updates

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    createdAt
    author {
      id
      posts(first: 1) {
        edges {
          node {
            id
          }
        }
        totalCount
      }
    }
  }
}

Error Handling

Query Result Unions

When schema uses union types for errors:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    ... on CreateUserSuccess {
      user {
        id
        email
      }
    }
    ... on ValidationError {
      message
      field
    }
    ... on EmailAlreadyExists {
      message
      existingUserId
    }
  }
}

Handle All Cases

const result = await client.mutate({
  mutation: CREATE_USER,
  variables: { input },
});

const { createUser } = result.data;

switch (createUser.__typename) {
  case "CreateUserSuccess":
    // Handle success
    return createUser.user;
  case "ValidationError":
    // Handle validation error
    throw new ValidationError(createUser.field, createUser.message);
  case "EmailAlreadyExists":
    // Handle specific business error
    throw new EmailExistsError(createUser.existingUserId);
}

GraphQL Errors

Handle network and GraphQL errors:

try {
  const result = await client.mutate({
    mutation: CREATE_POST,
    variables: { input },
  });
  return result.data.createPost;
} catch (error) {
  if (error.graphQLErrors?.length) {
    // Handle GraphQL errors
    const gqlError = error.graphQLErrors[0];
    if (gqlError.extensions?.code === "UNAUTHENTICATED") {
      // Redirect to login
    }
  }
  if (error.networkError) {
    // Handle network error
  }
  throw error;
}

Optimistic Updates

Select Fields for Optimistic Response

Include all fields that will display immediately:

mutation LikePost($postId: ID!) {
  likePost(postId: $postId) {
    id
    likeCount
    isLikedByViewer
  }
}
client.mutate({
  mutation: LIKE_POST,
  variables: { postId: "post_123" },
  optimisticResponse: {
    likePost: {
      __typename: "Post",
      id: "post_123",
      likeCount: currentCount + 1,
      isLikedByViewer: true,
    },
  },
});

Include Created IDs

For create mutations, use temporary IDs:

mutation AddComment($input: AddCommentInput!) {
  addComment(input: $input) {
    id
    body
    createdAt
    author {
      id
      name
      avatarUrl
    }
  }
}
client.mutate({
  mutation: ADD_COMMENT,
  variables: { input: { postId, body } },
  optimisticResponse: {
    addComment: {
      __typename: "Comment",
      id: `temp-${Date.now()}`, // Temporary ID
      body,
      createdAt: new Date().toISOString(),
      author: {
        __typename: "User",
        id: currentUser.id,
        name: currentUser.name,
        avatarUrl: currentUser.avatarUrl,
      },
    },
  },
});

Mutation Naming

Naming Conventions

Operation Pattern Examples
Create Create{Type} CreateUser, CreatePost
Update Update{Type} UpdateUser, UpdatePost
Delete Delete{Type} DeleteUser, DeletePost
Action {Verb}{Type} PublishPost, ArchiveProject
Relationship {Add/Remove}{Type} AddTeamMember, RemoveTag

Good Names

mutation CreateUser($input: CreateUserInput!) { ... }
mutation UpdateUserProfile($userId: ID!, $input: ProfileInput!) { ... }
mutation DeletePost($id: ID!) { ... }
mutation PublishArticle($id: ID!) { ... }
mutation ArchiveProject($id: ID!) { ... }
mutation AddItemToCart($input: AddItemInput!) { ... }
mutation RemoveTeamMember($teamId: ID!, $userId: ID!) { ... }
mutation FollowUser($userId: ID!) { ... }
mutation MarkNotificationAsRead($id: ID!) { ... }

Operation Name Matches Server

Match client operation name to server mutation:

# Server schema
type Mutation {
  createPost(input: CreatePostInput!): Post!
}

# Client operation - name reflects the action
mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
  }
}

Context-Specific Names

Add context when same mutation is used differently:

# For creating a draft
mutation CreateDraftPost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    status
  }
}

# For creating and publishing immediately
mutation CreateAndPublishPost($input: CreatePostInput!) {
  createPost(input: $input) {
    id
    title
    status
    publishedAt
  }
}

Source: SKILL.md on GitHub

1 warning2d5 checks · Risk SAFE
  • Gen Agent Trust Hub2d

    The skill is a comprehensive guide for writing GraphQL operations and setting up associated development tools. It contains only instructional content and standard developer workflow examples. No security risks were identified.

  • Socket2d

    No alerts

  • Snyk2d

    Risk: LOW · No issues

  • Runlayer7mo

    6/6 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 194056e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 2 days ago
What it can do
Runs commands
compatibility
Any GraphQL client (Apollo Client, urql, Relay, etc.)
metadata
{
  "author": "apollographql",
  "version": "1.0.1"
}
All 1 allowed tools
Bash(npm:*) Bash(npx:*) Read Write Edit Glob Grep
  • graphql
  • queries
  • mutations
  • fragments
  • apollo-client
  • type-generation
  • best-practices
  • variables

README badge

README badge for apollographql/skills/graphql-operations

Teaches best practices for writing GraphQL operations (queries, mutations, subscriptions) and organizing them with fragments. Covers naming conventions, variable usage, field selection optimization, and colocating fragments with components across Apollo Client and other GraphQL clients.

Generated from the current SKILL.md.

Does this skill work with Apollo Client, urql, and other GraphQL clients?
Yes. The skill covers GraphQL operation best practices that apply to any GraphQL client library, including Apollo Client, urql, Relay, and others.
Does this skill include code generation or type safety setup?
The skill mentions type generation and linting as areas it addresses, with references to a tooling documentation file, but focuses primarily on writing and organizing operations by hand.
Can I use this skill for subscriptions?
Yes. The skill covers queries, mutations, and subscriptions equally, with examples and naming conventions for each.
Does this skill help optimize data fetching?
Yes. It includes principles like requesting only the fields you need, using fragments to avoid duplication, and leveraging directives like @include and @skip for conditional fetching.

Generated from the current SKILL.md. These answers refresh after source changes.