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.

referencesqueries.md

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

Query Patterns

This reference covers patterns for writing effective GraphQL queries.

Table of Contents

Query Structure

Basic Query

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

Components:

  • query - Operation type
  • GetUser - Operation name
  • ($id: ID!) - Variable definitions
  • user(id: $id) - Field with argument
  • { id name email } - Selection set

Query with Multiple Root Fields

query GetDashboardData($userId: ID!) {
  user(id: $userId) {
    id
    name
  }
  notifications(first: 5) {
    id
    message
  }
  stats {
    totalPosts
    totalComments
  }
}

Nested Queries

query GetUserWithPosts($userId: ID!) {
  user(id: $userId) {
    id
    name
    posts(first: 10) {
      edges {
        node {
          id
          title
          comments(first: 3) {
            edges {
              node {
                id
                body
              }
            }
          }
        }
      }
    }
  }
}

Field Selection

Request Only Needed Fields

# For a user card component
query GetUserCard($id: ID!) {
  user(id: $id) {
    id
    name
    avatarUrl
    # Don't request email, bio, etc. if not displayed
  }
}

Always Include ID Fields

Include id for any type you might cache or refetch:

query GetPost($id: ID!) {
  post(id: $id) {
    id # Always include for caching
    title
    author {
      id # Include for author cache entry
      name
    }
  }
}

Selecting Connections

For paginated data, request what you need:

query GetUserPosts($userId: ID!, $first: Int!, $after: String) {
  user(id: $userId) {
    id
    posts(first: $first, after: $after) {
      edges {
        node {
          id
          title
          excerpt
        }
        cursor # Only if implementing infinite scroll
      }
      pageInfo {
        hasNextPage
        endCursor
      }
      totalCount # Only if displaying total
    }
  }
}

Aliases

Basic Alias

Rename fields in the response:

query GetUserNames($id: ID!) {
  user(id: $id) {
    userId: id
    displayName: name
  }
}

# Response: { user: { userId: "123", displayName: "John" } }

Query Same Field Multiple Times

query GetMultipleUsers {
  admin: user(id: "1") {
    id
    name
  }
  moderator: user(id: "2") {
    id
    name
  }
  currentUser: user(id: "3") {
    id
    name
  }
}

Alias with Different Arguments

query GetPostsByStatus($userId: ID!) {
  user(id: $userId) {
    id
    publishedPosts: posts(status: PUBLISHED, first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
    draftPosts: posts(status: DRAFT, first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
}

Directives

@include Directive

Include field only if condition is true:

query GetUser($id: ID!, $includeEmail: Boolean!) {
  user(id: $id) {
    id
    name
    email @include(if: $includeEmail)
  }
}

# Variables: { id: "123", includeEmail: true }
# Returns email field

# Variables: { id: "123", includeEmail: false }
# Does not return email field

@skip Directive

Skip field if condition is true:

query GetPost($id: ID!, $isPreview: Boolean!) {
  post(id: $id) {
    id
    title
    content @skip(if: $isPreview)
    excerpt
  }
}

Directives on Fragments

query GetUser($id: ID!, $expanded: Boolean!) {
  user(id: $id) {
    id
    name
    ...UserDetails @include(if: $expanded)
  }
}

fragment UserDetails on User {
  bio
  website
  socialLinks {
    platform
    url
  }
}

Combining Directives

query GetPost($id: ID!, $showComments: Boolean!, $hideAuthor: Boolean!) {
  post(id: $id) {
    id
    title
    author @skip(if: $hideAuthor) {
      id
      name
    }
    comments(first: 10) @include(if: $showComments) {
      edges {
        node {
          id
          body
        }
      }
    }
  }
}

Query Naming

Naming Conventions

Purpose Pattern Examples
Fetch single item Get{Type} GetUser, GetPost
Fetch list List{Types} ListUsers, ListPosts
Search Search{Types} SearchUsers, SearchProducts
Fetch for specific UI Get{Feature}Data GetDashboardData, GetProfilePage

Good Names

query GetUserProfile($id: ID!) { ... }
query ListRecentPosts($first: Int!) { ... }
query SearchProducts($query: String!) { ... }
query GetOrderDetails($orderId: ID!) { ... }
query GetHomeFeed($userId: ID!) { ... }

Avoid Generic Names

# Avoid
query Data { ... }
query Query1 { ... }
query FetchStuff { ... }

# Prefer
query GetCurrentUser { ... }
query ListActiveProjects { ... }
query SearchCustomers($query: String!) { ... }

Query Organization

One Query Per File

src/
  graphql/
    queries/
      GetUser.graphql
      ListPosts.graphql
      SearchProducts.graphql
# GetUser.graphql
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

Colocate with Components

src/
  components/
    UserProfile/
      UserProfile.tsx
      UserProfile.graphql
      UserProfile.test.tsx

Import and Use

// With graphql-tag
import { gql } from "@apollo/client";

export const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

// With .graphql files (requires loader)
import { GetUserDocument } from "./UserProfile.generated";

Performance Optimization

Avoid Over-fetching

Only request fields used by your component:

# For a list view - minimal fields
query ListPostsForIndex {
  posts(first: 20) {
    edges {
      node {
        id
        title
        excerpt
        author { name }
      }
    }
  }
}

# For detail view - more fields
query GetPostDetail($id: ID!) {
  post(id: $id) {
    id
    title
    content
    publishedAt
    author {
      id
      name
      bio
      avatarUrl
    }
    comments(first: 10) { ... }
  }
}

Use Pagination

Never fetch unbounded lists:

# Avoid
query GetAllPosts {
  posts {
    # Could return thousands
    id
    title
  }
}

# Prefer
query GetPosts($first: Int = 20, $after: String) {
  posts(first: $first, after: $after) {
    edges {
      node {
        id
        title
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Batch Related Queries

Fetch related data in one request:

# Instead of multiple queries
query GetDashboard($userId: ID!) {
  user(id: $userId) {
    id
    name
  }
  recentPosts: posts(first: 5, orderBy: { field: CREATED_AT, direction: DESC }) {
    edges {
      node {
        id
        title
      }
    }
  }
  notifications(first: 10, unreadOnly: true) {
    edges {
      node {
        id
        message
      }
    }
  }
}

Use Fragments for Repeated Selections

query GetPostsWithAuthors {
  posts(first: 10) {
    edges {
      node {
        id
        title
        author {
          ...AuthorInfo
        }
      }
    }
  }
  featuredPost {
    id
    title
    author {
      ...AuthorInfo
    }
  }
}

fragment AuthorInfo on User {
  id
  name
  avatarUrl
}

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.