{"owner":"apollographql","repo":"apollo-client","hasSkills":true,"totalSkillsCount":17,"totalTokensCount":38491,"categories":["command-prompt","claude-rule","anthropic-skill","cursor-rule"],"hasMcp":false,"mcpConfig":null,"found":[".claude/commands/chores.md","CLAUDE.md","docs/agent-skills/apollo-client/SKILL.md","docs/agent-skills/apollo-client/references/caching.md","docs/agent-skills/apollo-client/references/error-handling.md","docs/agent-skills/apollo-client/references/fragments.md","docs/agent-skills/apollo-client/references/integration-client.md","docs/agent-skills/apollo-client/references/integration-nextjs.md","docs/agent-skills/apollo-client/references/integration-react-router.md","docs/agent-skills/apollo-client/references/integration-tanstack-start.md","docs/agent-skills/apollo-client/references/mutations.md","docs/agent-skills/apollo-client/references/queries.md","docs/agent-skills/apollo-client/references/state-management.md","docs/agent-skills/apollo-client/references/suspense-hooks.md","docs/agent-skills/apollo-client/references/troubleshooting.md","docs/agent-skills/apollo-client/references/typescript-codegen.md",".cursorrules (react-graphql-apollo-client-cursorrules-prompt-file.mdc)"],"skills":{".claude/commands/chores.md":"---\nallowed-tools: Bash(git commit:*)\ndescription: Chores\n---\n\n## Context\n\n- Current git status: !`git status`\n- Current git diff (staged and unstaged changes): !`git diff HEAD`\n- Current branch: !`git branch --show-current`\n- Recent commits: !`git log --oneline -10`\n\n## Your task\n\nFetch the latest changes from the remote repository. If the current branch is not up to date, don't do anything, but inform the user about it.\n\nTake a note if the current git status is clean.\n\nRun the npm scripts `format`, `build`, `extract-api:only`, `update-size-limits` after each other in that order.\n\nIf before running the scripts, the git status was clean, create a new commit with the message \"chores\".\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Apollo Client Overview\n\nApollo Client is a comprehensive caching GraphQL client for TypeScript/JavaScript. The project is maintained at version 4.x.x and uses a monorepo structure with npm workspaces.\n\n## Common Development Commands\n\n### Build Commands\n\n- `npm run build` - Build the entire project\n- `npm run build -w codegen` - Build specific workspace (e.g., codegen)\n\n### Testing Commands\n\n- `npm test` - Run all tests with Jest\n- `npm run test:type-benches` - Run type benchmarks\n- `npm run test:memory` - Run memory tests\n\n### Code Quality Commands\n\n- `npm run typecheck` - Run TypeScript type checking\n- `npm run lint` - Run ESLint with extended rules\n- `npm run format` - Format code with Prettier\n- `npm run check:format` - Check if code is formatted correctly\n- `npm run knip` - Check for unused files/exports\n- `npm run madge` - Check for circular dependencies\n\n### Documentation & API\n\n- `npm run extract-api` - Generate API documentation\n- `npm run typedoc` - Generate TypeDoc documentation\n- `npm run docmodel` - Generate API documentation model\n\n### Release & Publishing\n\n- `npx changeset` - Create a changeset for your changes\n\n## High-Level Architecture\n\n### Core Modules\n\n1. **Core (`src/core/`)** - Main Apollo Client implementation\n\n   - `ApolloClient.ts` - Main client class\n   - `ObservableQuery.ts` - Observable query implementation\n   - `QueryManager.ts` - Query lifecycle management\n\n2. **Cache (`src/cache/`)** - Caching layer\n\n   - `inmemory/` - InMemoryCache implementation (default cache)\n   - `core/` - Base cache abstractions\n\n3. **Link (`src/link/`)** - Network layer abstraction\n\n   - `http/` - HTTP link for GraphQL over HTTP\n   - `batch/` - Batch link for combining queries\n   - `error/` - Error handling link\n   - `schema/` - Schema link for local execution\n   - `ws/` - WebSocket link for subscriptions\n\n4. **React Integration (`src/react/`)** - React hooks and components\n\n   - `hooks/` - React hooks (useQuery, useMutation, etc.)\n   - `ssr/` - Server-side rendering support\n   - `context/` - React context providers\n\n5. **Utilities (`src/utilities/`)** - Shared utilities\n\n   - `graphql/` - GraphQL document utilities\n   - `internal/` - Internal utilities\n   - `invariant/` - Error handling utilities\n\n6. **Testing (`src/testing/`)** - Testing utilities\n\n   - `core/` - Core testing utilities\n   - `react/` - React testing utilities (MockedProvider)\n\n7. **Masking (`src/masking/`)** - Data masking for fragment colocation\n\n8. **Local State (`src/local-state/`)** - Local state management\n\n### TypeScript Configuration\n\n- Uses TypeScript 5.7.3 with strict mode enabled\n- Module resolution: NodeNext\n- Target: ESNext\n- Separate tsconfig files for different purposes:\n  - `tsconfig.json` - Main configuration\n  - `tsconfig.build.json` - Build configuration\n  - `tsconfig.tests.json` - Test configuration\n\n### Testing Infrastructure\n\n- Jest with ts-jest for testing\n- Custom test environment extending jsdom\n- React Testing Library for React component tests\n- Multiple React version testing (17, 18, 19)\n- Coverage reporting with lcov\n\n### Build System\n\n- Uses custom build script (`config/build.ts`)\n- API Extractor for API documentation\n- Size limit checking for bundle size\n- Source map verification\n- Publint for package publishing validation\n\n### Code Organization Patterns\n\n1. **Exports** - All public exports go through index files\n2. **Internal APIs** - Use `@apollo/client/*/internal` paths\n3. **Environment-specific builds** - Production/development variants\n4. **Type safety** - Heavy use of TypeScript generics and type inference\n5. **Reactive programming** - Uses RxJS for observables\n6. **Immutability** - Emphasizes immutable data structures\n\n### Contributing Guidelines\n\n- All changes require a changeset (`npx changeset`)\n- PRs should include tests for bug fixes\n- Follow existing code style (enforced by ESLint/Prettier)\n- Large features need design discussion first\n- Code review required from core contributors\n\n### Important Notes\n\n- This is a monorepo with workspaces: main package, codegen, and codemods\n- The project is currently at version 4.x.x (release candidate)\n- Supports multiple peer dependencies (React 17/18/19, GraphQL 16)\n- Uses patch-package for dependency patches\n- Has extensive CI checks that must pass before merging\n\n## Additional Instructions\n\n@.claude/documentation.md\n@.claude/hooks.md\n","docs/agent-skills/apollo-client/SKILL.md":"---\nname: apollo-client\ndescription: >\n  Guide for building React applications with Apollo Client 4.x. Use this skill when:\n  (1) setting up Apollo Client in a React project,\n  (2) writing GraphQL queries or mutations with hooks,\n  (3) configuring caching or cache policies,\n  (4) managing local state with reactive variables,\n  (5) troubleshooting Apollo Client errors or performance issues.\nlicense: MIT\ncompatibility: React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.\nmetadata:\n  author: apollographql\n  version: \"1.0.0\"\nallowed-tools: Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep\n---\n\n# Apollo Client 4.x Guide\n\nApollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Version 4.x brings improved caching, better TypeScript support, and React 19 compatibility.\n\n## Integration Guides\n\nChoose the integration guide that matches your application setup:\n\n- **[Client-Side Apps](references/integration-client.md)** - For client-side React applications without SSR (Vite, Create React App, etc.)\n- **[Next.js App Router](references/integration-nextjs.md)** - For Next.js applications using the App Router with React Server Components\n- **[React Router Framework Mode](references/integration-react-router.md)** - For React Router 7 applications with streaming SSR\n- **[TanStack Start](references/integration-tanstack-start.md)** - For TanStack Start applications with modern routing\n\nEach guide includes installation steps, configuration, and framework-specific patterns optimized for that environment.\n\n## Quick Reference\n\n### Basic Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n### Basic Mutation\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst CREATE_USER = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (name: string) => {\n    await createUser({ variables: { input: { name } } });\n  };\n\n  return <button onClick={() => handleSubmit(\"John\")}>Create User</button>;\n}\n```\n\n### Suspense Query\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<p>Loading user...</p>}>\n      <UserProfile userId=\"1\" />\n    </Suspense>\n  );\n}\n```\n\n## Reference Files\n\nDetailed documentation for specific topics:\n\n- [TypeScript Code Generation](references/typescript-codegen.md) - GraphQL Code Generator setup for type-safe operations\n- [Queries](references/queries.md) - useQuery, useLazyQuery, polling, refetching\n- [Suspense Hooks](references/suspense-hooks.md) - useSuspenseQuery, useBackgroundQuery, useReadQuery, useLoadableQuery\n- [Mutations](references/mutations.md) - useMutation, optimistic UI, cache updates\n- [Fragments](references/fragments.md) - Fragment colocation, useFragment, useSuspenseFragment, data masking\n- [Caching](references/caching.md) - InMemoryCache, typePolicies, cache manipulation\n- [State Management](references/state-management.md) - Reactive variables, local state\n- [Error Handling](references/error-handling.md) - Error policies, error links, retries\n- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions\n\n## Key Rules\n\n### Query Best Practices\n\n- **Each page should generally only have one query, composed from colocated fragments.** Use `useFragment` or `useSuspenseFragment` in all non-page-components. Use `@defer` to allow slow fields below the fold to stream in later and avoid blocking the page load.\n- **Fragments are for colocation, not reuse.** Each fragment should describe exactly the data needs of a specific component, not be shared across components for common fields. See [Fragments reference](references/fragments.md) for details on fragment colocation and data masking.\n- Always handle `loading` and `error` states in UI when using non-suspenseful hooks (`useQuery`, `useLazyQuery`). When using Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n- Use `fetchPolicy` to control cache behavior per query\n- Use the TypeScript type server to look up documentation for functions and options (Apollo Client has extensive docblocks)\n\n### Mutation Best Practices\n\n- **If the schema permits, mutation return values should return everything necessary to update the cache.** Neither manual updates nor refetching should be necessary.\n- If the mutation response is insufficient, carefully weigh manual cache manipulation vs refetching. Manual updates risk missing server logic. Consider optimistic updates with a granular refetch if needed.\n- Handle errors gracefully in the UI\n- Use `refetchQueries` sparingly (prefer letting the cache update automatically)\n\n### Caching Best Practices\n\n- Configure `keyFields` for types without `id` field\n- Disable normalization by setting `keyFields: false` for types that don't include an identifier and are meant to group related fields under the parent\n- Use `typePolicies` for pagination and computed fields\n- Understand cache normalization to debug issues\n- **Enable [data masking](references/fragments.md#data-masking) for all new applications** - it prevents components from accessing fragment data they don't own, enforcing proper data boundaries and preventing over-rendering\n\n### Performance\n\n- Avoid over-fetching with proper field selection\n- Configure appropriate `fetchPolicy` per use case\n- Use `@defer` for incremental delivery of deferred query parts, and `@stream` for streaming list fields (`@stream` available in Apollo Client 4.1+)\n- **Prefer Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications** for better loading state handling and code simplicity\n\n## Ground Rules\n\n- ALWAYS use Apollo Client 4.x patterns (not v3 or earlier)\n- ALWAYS wrap your app with `ApolloProvider`\n- ALWAYS handle loading and error states when using non-suspenseful hooks\n- PREFER Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications for better DX\n- NEVER store Apollo Client in React state (use module-level or context)\n- PREFER `cache-first` for read-heavy data, `network-only` for real-time data\n- USE TypeScript for better type safety with GraphQL\n- IMPLEMENT proper cache updates instead of refetching entire queries\n- ADVISE the human steering you to look into Apollo DevTools when collaboratively debugging Apollo Client issues\n","docs/agent-skills/apollo-client/references/caching.md":"# Caching Reference\n\n## Table of Contents\n\n- [InMemoryCache Setup](#inmemorycache-setup)\n- [Cache Normalization](#cache-normalization)\n- [Type Policies](#type-policies)\n- [Field Policies](#field-policies)\n- [Pagination](#pagination)\n- [Cache Manipulation](#cache-manipulation)\n- [Garbage Collection](#garbage-collection)\n\n## InMemoryCache Setup\n\n### Basic Configuration\n\n```typescript\nimport { InMemoryCache } from \"@apollo/client\";\n\nconst cache = new InMemoryCache({\n  // Custom type policies\n  typePolicies: {\n    Query: {\n      fields: {\n        // Query-level field policies\n      },\n    },\n    User: {\n      keyFields: [\"id\"],\n      fields: {\n        // User-level field policies\n      },\n    },\n  },\n\n  // Custom type name handling (rare)\n  possibleTypes: {\n    Character: [\"Human\", \"Droid\"],\n    Node: [\"User\", \"Post\", \"Comment\"],\n  },\n});\n```\n\n### Constructor Options\n\n```typescript\nnew InMemoryCache({\n  // Define how types are identified in cache\n  typePolicies: {\n    /* ... */\n  },\n\n  // Interface/union type mappings between supertypes and their subtypes\n  possibleTypes: {\n    /* ... */\n  },\n\n  // Custom function to generate cache IDs (rare)\n  dataIdFromObject: (object) => {\n    if (object.__typename === \"Book\") {\n      return `Book:${object.isbn}`;\n    }\n    return defaultDataIdFromObject(object);\n  },\n});\n```\n\n## Cache Normalization\n\nApollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.\n\n### How Normalization Works\n\n```graphql\n# Query\nquery GetPost {\n  post(id: \"1\") {\n    id\n    title\n    author {\n      id\n      name\n    }\n  }\n}\n```\n\n```typescript\n// Normalized cache structure\n{\n  'Post:1': {\n    __typename: 'Post',\n    id: '1',\n    title: 'Hello World',\n    author: { __ref: 'User:42' }\n  },\n  'User:42': {\n    __typename: 'User',\n    id: '42',\n    name: 'John'\n  },\n  ROOT_QUERY: {\n    'post({\"id\":\"1\"})': { __ref: 'Post:1' }\n  }\n}\n```\n\n### Benefits of Normalization\n\n1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update\n2. **Deduplication**: Same objects aren't stored multiple times\n3. **Efficient updates**: Only changed objects trigger re-renders\n\n## Type Policies\n\n### keyFields\n\nCustomize how objects are identified in the cache.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Use ISBN instead of id for books\n    Book: {\n      keyFields: [\"isbn\"],\n    },\n\n    // Composite key\n    UserSession: {\n      keyFields: [\"userId\", \"deviceId\"],\n    },\n\n    // Nested key\n    Review: {\n      keyFields: [\"book\", [\"isbn\"], \"reviewer\", [\"id\"]],\n    },\n\n    // No key fields (singleton, only one object in cache per type)\n    AppSettings: {\n      keyFields: [],\n    },\n\n    // Disable normalization (objects of this type will be stored with their\n    // parent entity. The same object might end up multiple times in the cache\n    // and run out of sync. Use with caution, only if this object really relates\n    // to a property of their parent entity and cannot exist on its own.)\n    Address: {\n      keyFields: false,\n    },\n  },\n});\n```\n\n### merge Functions\n\nControl how incoming data merges with existing data.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Deep merge profile object\n        profile: {\n          merge: true, // Shorthand for deep merge\n        },\n\n        // Custom merge logic\n        notifications: {\n          merge(existing = [], incoming, { mergeObjects }) {\n            // Prepend new notifications\n            return [...incoming, ...existing];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## Field Policies\n\n### read Function\n\nTransform cached data when reading.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Computed field\n        fullName: {\n          read(_, { readField }) {\n            const firstName = readField(\"firstName\");\n            const lastName = readField(\"lastName\");\n            return `${firstName} ${lastName}`;\n          },\n        },\n\n        // Transform existing field\n        birthDate: {\n          read(existing) {\n            return existing ? new Date(existing) : null;\n          },\n        },\n\n        // Default value\n        role: {\n          read(existing = \"USER\") {\n            return existing;\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### merge Function\n\nControl how incoming data is stored.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Accumulate items instead of replacing\n        friends: {\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n\n        // Merge objects deeply\n        settings: {\n          merge(existing, incoming, { mergeObjects }) {\n            return mergeObjects(existing, incoming);\n          },\n        },\n      },\n    },\n\n    Query: {\n      fields: {\n        // Merge paginated results\n        posts: {\n          keyArgs: [\"category\"], // Only category affects cache key\n          merge(existing = { items: [] }, incoming) {\n            return {\n              ...incoming,\n              items: [...existing.items, ...incoming.items],\n            };\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### keyArgs\n\nControl which arguments affect cache storage.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        // Different cache entry per userId only\n        // (limit, offset don't create new entries)\n        userPosts: {\n          keyArgs: [\"userId\"],\n        },\n\n        // No arguments affect cache key\n        // (useful for pagination)\n        feed: {\n          keyArgs: false,\n        },\n\n        // Nested argument\n        search: {\n          keyArgs: [\"filter\", [\"category\", \"status\"]],\n        },\n      },\n    },\n  },\n});\n```\n\n## Pagination\n\n### Offset-Based Pagination\n\n```typescript\nimport { offsetLimitPagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: offsetLimitPagination(),\n\n        // With key arguments\n        userPosts: offsetLimitPagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Cursor-Based Pagination (Relay Style)\n\n```typescript\nimport { relayStylePagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: relayStylePagination(),\n\n        // With key arguments\n        userPosts: relayStylePagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Custom Pagination\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        feed: {\n          keyArgs: false,\n\n          merge(existing, incoming, { args }) {\n            const merged = existing ? existing.slice(0) : [];\n            const offset = args?.offset ?? 0;\n\n            for (let i = 0; i < incoming.length; i++) {\n              merged[offset + i] = incoming[i];\n            }\n\n            return merged;\n          },\n\n          read(existing, { args }) {\n            const offset = args?.offset ?? 0;\n            const limit = args?.limit ?? existing?.length ?? 0;\n            return existing?.slice(offset, offset + limit);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### fetchMore for Pagination\n\n```tsx\nfunction PostList() {\n  const { data, fetchMore, loading } = useQuery(GET_POSTS, {\n    variables: { offset: 0, limit: 10 },\n  });\n\n  const loadMore = () => {\n    fetchMore({\n      variables: {\n        offset: data.posts.length,\n      },\n      // With proper type policies, no updateQuery needed\n    });\n  };\n\n  return (\n    <div>\n      {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}\n      <button onClick={loadMore} disabled={loading}>\n        Load More\n      </button>\n    </div>\n  );\n}\n```\n\n## Cache Manipulation\n\n### cache.readQuery\n\n```typescript\n// Read data from cache\nconst data = cache.readQuery({\n  query: GET_TODOS,\n});\n\n// With variables\nconst userData = cache.readQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n});\n```\n\n### cache.writeQuery\n\n```typescript\n// Write data to cache\ncache.writeQuery({\n  query: GET_TODOS,\n  data: {\n    todos: [\n      { __typename: \"Todo\", id: \"1\", text: \"Buy milk\", completed: false },\n    ],\n  },\n});\n\n// With variables\ncache.writeQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n  data: {\n    user: { __typename: \"User\", id: \"1\", name: \"John\" },\n  },\n});\n```\n\n### cache.readFragment / cache.writeFragment\n\n```typescript\n// Read a specific object - use cache.identify for safety\nconst user = cache.readFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\nconst user = cache.readFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Update a specific object\ncache.writeFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\ncache.writeFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n```\n\n### cache.modify\n\n```typescript\n// Modify fields directly\ncache.modify({\n  id: cache.identify(user),\n  fields: {\n    // Set new value\n    name: () => \"New Name\",\n\n    // Transform existing value\n    postCount: (existing) => existing + 1,\n\n    // Delete field\n    temporaryField: (_, { DELETE }) => DELETE,\n\n    // Add to array\n    friends: (existing, { toReference }) => [\n      ...existing,\n      toReference({ __typename: \"User\", id: \"2\" }),\n    ],\n  },\n});\n```\n\n### cache.evict\n\n```typescript\n// Remove object from cache\ncache.evict({ id: \"User:1\" });\n\n// Remove specific field\ncache.evict({ id: \"User:1\", fieldName: \"friends\" });\n\n// Remove with broadcast (trigger re-renders)\ncache.evict({ id: \"User:1\", broadcast: true });\n```\n\n## Garbage Collection\n\n### Manual Garbage Collection\n\n```typescript\n// After evicting objects, clean up dangling references\ncache.evict({ id: \"User:1\" });\ncache.gc();\n```\n\n### Retaining Objects\n\n```typescript\n// Prevent objects from being garbage collected\nconst release = cache.retain(\"User:1\");\n\n// Later, allow GC\nrelease();\ncache.gc();\n```\n\n### Inspecting Cache\n\n```typescript\n// Get all cached data\nconst cacheContents = cache.extract();\n\n// Restore cache state\ncache.restore(previousCacheContents);\n\n// Get identified object cache key\nconst userId = cache.identify({ __typename: \"User\", id: \"1\" });\n// Returns: 'User:1'\n```\n","docs/agent-skills/apollo-client/references/error-handling.md":"# Error Handling Reference (Apollo Client 4.x)\n\nNote that Apollo Client 4.x handles errors differently than Apollo Client 3.x.\nThis reference documents the updated error handling mechanisms, error types, and best practices for managing errors in your Apollo Client applications.\nFor older Apollo Client 3.x error handling documentation, see [Apollo Client 3.x Error Handling](https://www.apollographql.com/docs/react/v3/data/error-handling).\n\n## Table of Contents\n\n- [Understanding Errors](#understanding-errors)\n- [Error Types](#error-types)\n- [Identifying Error Types](#identifying-error-types)\n- [GraphQL Error Policies](#graphql-error-policies)\n- [Error Links](#error-links)\n- [Retry Link](#retry-link)\n- [Error Boundaries](#error-boundaries)\n\n## Understanding Errors\n\nErrors in Apollo Client fall into two main categories: **GraphQL errors** and **network errors**. Each category has specific error classes that provide detailed information about what went wrong.\n\n### GraphQL Errors\n\nGraphQL errors are related to server-side execution of a GraphQL operation:\n\n- **Syntax errors** (e.g., malformed query)\n- **Validation errors** (e.g., query includes a non-existent schema field)\n- **Resolver errors** (e.g., error while populating a query field)\n\nIf a syntax or validation error occurs, the server doesn't execute the operation. If resolver errors occur, the server can still return partial data.\n\nExample server response with GraphQL error:\n\n```json\n{\n  \"errors\": [\n    {\n      \"message\": \"Cannot query field \\\"nonexistentField\\\" on type \\\"Query\\\".\",\n      \"locations\": [{ \"line\": 2, \"column\": 3 }],\n      \"extensions\": {\n        \"code\": \"GRAPHQL_VALIDATION_FAILED\"\n      }\n    }\n  ],\n  \"data\": null\n}\n```\n\nIn Apollo Client 4.x, GraphQL errors are represented by the [`CombinedGraphQLErrors`](https://apollographql.com/docs/react/api/errors/CombinedGraphQLErrors) error type.\n\n### Network Errors\n\nNetwork errors occur when attempting to communicate with your GraphQL server:\n\n- `4xx` or `5xx` HTTP response status codes\n- Network unavailability\n- JSON parsing failures\n- Custom errors from Apollo Link request handlers\n\nNetwork errors might be represented by special error types, but if an api such as the `fetch` API throws a native error (e.g., `TypeError`), Apollo Client will pass it through as-is.\nThrown values that don't fulfill the standard `ErrorLike` interface are wrapped in the [`UnconventionalError`](https://apollographql.com/docs/react/api/errors/UnconventionalError) class, which fulfills the `ErrorLike` interface. As such, you can expect any error returned by Apollo Client to fulfill the `ErrorLike` interface.\n\n```ts\nexport interface ErrorLike {\n  message: string;\n  name: string;\n  stack?: string;\n}\n```\n\n## Error Types\n\nApollo Client 4.x provides specific error classes for different error scenarios:\n\n### CombinedGraphQLErrors\n\nRepresents GraphQL errors returned by the server. Most common error type in applications.\n\n```tsx\nimport { CombinedGraphQLErrors } from \"@apollo/client/errors\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, error } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // no need to check for nullishness of error, CombinedGraphQLErrors.is handles that\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    return (\n      <div>\n        {error.graphQLErrors.map((err, i) => (\n          <p key={i}>GraphQL Error: {err.message}</p>\n        ))}\n      </div>\n    );\n  }\n\n  return data ? <Profile user={data.user} /> : null;\n}\n```\n\n### CombinedProtocolErrors\n\nRepresents fatal transport-level errors during multipart HTTP subscription execution.\n\n### ServerError\n\nOccurs when the server responds with a non-200 HTTP status code.\n\n```tsx\nimport { ServerError } from \"@apollo/client/errors\";\n\nif (ServerError.is(error)) {\n  console.error(\"Server error:\", error.statusCode, error.result);\n}\n```\n\n### ServerParseError\n\nOccurs when the server response cannot be parsed as valid JSON.\n\n```tsx\nimport { ServerParseError } from \"@apollo/client/errors\";\n\nif (ServerParseError.is(error)) {\n  console.error(\"Invalid JSON response:\", error.bodyText);\n}\n```\n\n### LocalStateError\n\nRepresents errors in local state configuration or execution.\n\n### UnconventionalError\n\nWraps non-standard errors (e.g., thrown symbols or objects) to ensure consistent error handling.\n\n## Identifying Error Types\n\nEvery Apollo Client error class provides a static `is` method that reliably determines whether an error is of that specific type. This is more robust than `instanceof` because it avoids false positives/negatives.\n\n```ts\nimport {\n  CombinedGraphQLErrors,\n  CombinedProtocolErrors,\n  LocalStateError,\n  ServerError,\n  ServerParseError,\n  UnconventionalError,\n  ErrorLike,\n} from \"@apollo/client/errors\";\n\n// Anything returned in the `error` field of Apollo Client hooks or methods is of type `ErrorLike` or `undefined`.\nfunction handleError(error?: ErrorLike) {\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    console.error(\"GraphQL errors:\", error.graphQLErrors);\n  } else if (CombinedProtocolErrors.is(error)) {\n    // Handle multipart subscription protocol errors\n  } else if (LocalStateError.is(error)) {\n    // Handle errors thrown by the LocalState class\n  } else if (ServerError.is(error)) {\n    // Handle server HTTP errors\n    console.error(\"Server error:\", error.statusCode);\n  } else if (ServerParseError.is(error)) {\n    // Handle JSON parse errors\n  } else if (UnconventionalError.is(error)) {\n    // Handle errors thrown by irregular types\n  } else if (error) {\n    // Handle other errors\n  }\n}\n```\n\n## GraphQL Error Policies\n\nIf a GraphQL operation produces errors, the server's response might still include partial data:\n\n```json\n{\n  \"data\": {\n    \"getInt\": 12,\n    \"getString\": null\n  },\n  \"errors\": [\n    {\n      \"message\": \"Failed to get string!\"\n    }\n  ]\n}\n```\n\nBy default, Apollo Client throws away partial data and populates the `error` field. You can use partial results by defining an **error policy**:\n\n| Policy   | Description                                                                                                                                                                  |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `none`   | (Default) If the response includes errors, they are returned in `error` and response `data` is set to `undefined` even if the server returns `data`.                         |\n| `ignore` | Errors are ignored (`error` is not populated), and any returned `data` is cached and rendered as if no errors occurred. `data` may be `undefined` if a network error occurs. |\n| `all`    | Both `data` and `error` are populated and any returned `data` is cached, enabling you to render both partial results and error information.                                  |\n\n### Setting an Error Policy\n\n```tsx\nconst MY_QUERY = gql`\n  query WillFail {\n    badField # This field's resolver produces an error\n    goodField # This field is populated successfully\n  }\n`;\n\nfunction ShowingSomeErrors() {\n  const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: \"all\" });\n\n  if (loading) return <span>loading...</span>;\n\n  return (\n    <div>\n      <h2>Good: {data?.goodField}</h2>\n      {error && <pre>Bad: {error.message}</pre>}\n    </div>\n  );\n}\n```\n\n### Avoid setting a Global Error Policy\n\nWhile it is possible to set a global error policy using `defaultOptions`, in practice this is discouraged as it can lead to unexpected behavior and type safety issues. The return types of the TypeScript hooks may change depending on the `errorPolicy` passed into the hook, and this can conceptually not take global `defaultOptions` error policies into account. As such, it is best to set the `errorPolicy` per operation as needed.\n\n## Error Links\n\nThe `ErrorLink` can be used to e.g. log error globally or perform specific side effects based on errors happening.\n\nAn `ErrorLink` can't be used to swallow errors fully, but it can be used to retry an operation after handling an error, in which case the error wouldn't propagate. Otherwise, the most common use for `ErrorLink` is logging.\n\n```ts\nimport { ErrorLink } from \"@apollo/client/link/error\";\n\nconst errorLink = new ErrorLink(({ error, operation, forward }) => {\n  if (someCondition(error)) {\n    // Retry the request, returning the new observable\n    return forward(operation);\n  }\n\n  // Log the error for any unhandled GraphQL errors or network errors.\n  console.log(`[Error]: ${error.message}`);\n\n  // If nothing is returned from the error handler callback, the error will be\n  // emitted from the link chain as normal.\n});\n```\n\n### Retry Link\n\nAlternatively, you can use the `RetryLink` from `@apollo/client/link/retry` to implement retry logic for failed operations.\n\n```typescript\nimport { RetryLink } from \"@apollo/client/link/retry\";\n\nconst retryLink = new RetryLink({\n  delay: {\n    initial: 300,\n    max: Infinity,\n    jitter: true,\n  },\n  attempts: {\n    max: 5,\n    retryIf: (error, operation) => {\n      // Retry on network errors\n      return !!error && operation.operationName !== \"SensitiveOperation\";\n    },\n  },\n});\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  link: from([retryLink, errorLink, httpLink]),\n});\n```\n\n### Custom Retry Logic\n\n```typescript\nconst retryLink = new RetryLink({\n  attempts: (count, operation, error) => {\n    // Don't retry mutations\n    if (\n      operation.query.definitions.some(\n        (def) =>\n          def.kind === \"OperationDefinition\" && def.operation === \"mutation\"\n      )\n    ) {\n      return false;\n    }\n\n    // Retry up to 3 times on network errors\n    return count < 3 && !!error;\n  },\n  delay: (count) => {\n    // Exponential backoff\n    return Math.min(1000 * Math.pow(2, count), 30000);\n  },\n});\n```\n\n## Error Boundaries\n\nWhen using suspenseful hooks, you should use React Error Boundaries for graceful error handling.\n\n### Non-suspense per-Component Error Handling\n\n```tsx\nimport {\n  CombinedGraphQLErrors,\n  ServerError,\n  ServerParseError,\n} from \"@apollo/client/errors\";\n\nfunction SafeUserList() {\n  const { data, error, loading, refetch } = useQuery(GET_USERS, {\n    errorPolicy: \"all\",\n    notifyOnNetworkStatusChange: true,\n  });\n\n  // Handle network errors\n  if (ServerError.is(error) || ServerParseError.is(error)) {\n    return (\n      <Alert severity=\"error\">\n        <AlertTitle>Connection Error</AlertTitle>\n        Failed to load users. Please check your internet connection.\n        <Button onClick={() => refetch()}>Retry</Button>\n      </Alert>\n    );\n  }\n\n  // Handle GraphQL errors but still show available data\n  return (\n    <div>\n      {CombinedGraphQLErrors.is(error) && (\n        <Alert severity=\"warning\">\n          Some data may be incomplete: {error.graphQLErrors[0].message}\n        </Alert>\n      )}\n\n      {loading && <LinearProgress />}\n\n      {data?.users && <UserList users={data.users} />}\n    </div>\n  );\n}\n```\n","docs/agent-skills/apollo-client/references/fragments.md":"# Fragments Reference\n\nGraphQL fragments define a set of fields for a specific type. In Apollo Client, fragments are especially powerful when colocated with components to define each component's data requirements independently, creating a clear separation of concerns and enabling better component composition.\n\n## Table of Contents\n\n- [What Are Fragments](#what-are-fragments)\n- [Basic Fragment Syntax](#basic-fragment-syntax)\n- [Fragment Colocation](#fragment-colocation)\n- [Fragment Reading Hooks](#fragment-reading-hooks)\n- [Data Masking](#data-masking)\n- [Fragment Registry](#fragment-registry)\n- [TypeScript Integration](#typescript-integration)\n- [Best Practices](#best-practices)\n\n## What Are Fragments\n\nA GraphQL fragment defines a set of fields for a specific GraphQL type. Fragments are defined on a specific GraphQL type and can be included in operations using the spread operator (`...`).\n\nIn Apollo Client, fragments serve a specific purpose:\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment. This allows components to independently evolve their data requirements without creating artificial dependencies between unrelated parts of your application.\n\nFragments enable:\n\n1. **Component colocation**: Define the exact data requirements for a component alongside the component code\n2. **Independent evolution**: Change a component's data needs without affecting other components\n3. **Code organization**: Compose fragments together to build complete queries that mirror your component hierarchy\n\n## Basic Fragment Syntax\n\n### Defining a Fragment\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n    avatarUrl\n  }\n`;\n```\n\nEvery fragment includes:\n\n- A unique name (`UserFields`)\n- The type it operates on (`User`)\n- The fields to select\n\n### Using Fragments in Queries\n\nInclude fragments in queries using the spread operator:\n\n```typescript\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserFields\n    }\n  }\n\n  ${USER_FRAGMENT}\n`;\n```\n\nWhen using GraphQL Code Generator with the recommended configuration (typescript, typescript-operations, and typed-document-node plugins), fragments defined in your source files are automatically picked up and generated into typed document nodes. The generated fragment documents already include the fragment definition, so you don't need to interpolate them manually into queries.\n\n## Fragment Colocation\n\nFragment colocation is the practice of defining fragments in the same file as the component that uses them. This creates a clear contract between components and their data requirements.\n\n### Why Colocate Fragments\n\n- **Locality**: Data requirements live next to the code that uses them\n- **Maintainability**: Changes to component UI and data needs happen together\n- **Type safety**: TypeScript can infer exact types from colocated fragments\n- **Independence**: Components can evolve their data requirements without affecting other components\n\n### Colocation Pattern\n\nThe recommended pattern for colocating fragments with components:\n\n```tsx\nimport { gql, FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\n\n// Fragment definition\n// This will be picked up by Codegen to create `UserCard_UserFragmentDoc` in `./fragments.generated.ts`.\n// As that generated fragment document is correctly typed, we use that in the code going forward.\n// This fragment will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n}\n\n// This has been created from above fragment definition by CodeGen and is a correctly typed `TypedDocumentNode`\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated.ts\";\n\n// Component receives the (partially masked) parent object\nexport function UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a subscription to the fragment in the cache\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return (\n    <div>\n      <img src={data.avatarUrl} alt={data.name} />\n      <h2>{data.name}</h2>\n      <p>{data.email}</p>\n    </div>\n  );\n}\n```\n\n### Naming Convention\n\nA suggested naming pattern for fragments follows this convention:\n\n```text\n{ComponentName}_{propName}\n```\n\nWhere `propName` is the name of the prop the component receives containing the fragment data.\n\nExamples:\n\n- `UserCard_user` - Fragment for the `user` prop in the UserCard component\n- `PostList_posts` - Fragment for the `posts` prop in the PostList component\n- `CommentItem_comment` - Fragment for the `comment` prop in the CommentItem component\n\nThis convention makes it clear which component owns which fragment. However, you can choose a different naming convention based on your project's needs.\n\n**Note**: A component might accept fragment data through multiple props, in which case it would have multiple associated fragments. For example, a `CommentCard` component might accept both a `comment` prop and an `author` prop, resulting in `CommentCard_comment` and `CommentCard_author` fragments.\n\n### Composing Fragments\n\nParent components compose child fragments to build complete queries:\n\n```tsx\n// Child component\nimport { gql } from \"@apollo/client\";\n\nif (false) {\n  gql`\n    fragment UserAvatar_user on User {\n      id\n      avatarUrl\n      name\n    }\n  `;\n}\n\n// Parent component composes child fragments\nif (false) {\n  gql`\n    fragment UserProfile_user on User {\n      id\n      name\n      email\n      bio\n      ...UserAvatar_user\n    }\n  `;\n}\n\n// Page-level query composes all fragments\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserProfile_user\n      }\n    }\n  `;\n}\n```\n\nThis creates a hierarchy that mirrors your component tree.\n\n## Fragment Reading Hooks\n\nApollo Client provides hooks to read fragment data within components. These hooks work with data masking to ensure components only access the data they explicitly requested.\n\n### useSuspenseFragment\n\nFor components using Suspense and concurrent features:\n\n```tsx\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return <div>{data.name}</div>;\n}\n```\n\n### useFragment\n\nFor components not using Suspense:\n\n```tsx\nimport { useFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data, complete } = useFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  if (!complete) {\n    return <div>Loading...</div>;\n  }\n\n  return <div>{data.name}</div>;\n}\n```\n\nThe `complete` field indicates whether all fragment data is available in the cache.\n\n### Hook Options\n\nBoth hooks accept these options:\n\n```typescript\n{\n  // The fragment document (required)\n  fragment: TypedDocumentNode,\n\n  // The fragment name (optional in most cases)\n  // Only required if the fragment document contains multiple definitions\n  fragmentName?: string,\n\n  // The source data containing the fragment (required)\n  // Can be a single object or an array of objects\n  from: FragmentType<typeof fragment> | Array<FragmentType<typeof fragment>>,\n\n  // Variables for the fragment (optional)\n  variables?: Variables,\n}\n```\n\nWhen `from` is an array, the hook returns an array of results, allowing you to read fragments from multiple objects efficiently. **Note**: Array support for the `from` parameter was added in Apollo Client 4.1.0.\n\n## Data Masking\n\nData masking is a feature that prevents components from accessing data they didn't explicitly request through their fragments. This enforces proper data boundaries and prevents over-rendering.\n\n### Enabling Data Masking\n\nEnable data masking when creating your Apollo Client:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true, // Enable data masking\n});\n```\n\n### How Data Masking Works\n\nWith data masking enabled:\n\n1. Fragments return opaque `FragmentType` objects\n2. Components must use `useFragment` or `useSuspenseFragment` to unmask data\n3. Components can only access fields defined in their own fragments\n4. TypeScript enforces these boundaries at compile time\n\nWithout data masking:\n\n```tsx\n// ❌ Without data masking - component can access any data from parent\nfunction UserCard({ user }: { user: User }) {\n  // Can access any User field, even if not in fragment\n  return <div>{user.privateData}</div>;\n}\n```\n\nWith data masking:\n\n```tsx\n// ✅ With data masking - component can only access its fragment data\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // TypeScript error: 'privateData' doesn't exist on fragment type\n  // return <div>{data.privateData}</div>;\n\n  // Only fields from the fragment are accessible\n  return <div>{data.name}</div>;\n}\n```\n\n### Benefits of Data Masking\n\n- **Prevents over-rendering**: Components only re-render when their specific data changes\n- **Enforces boundaries**: Components can't accidentally depend on data they don't own\n- **Better refactoring**: Safe to modify parent queries without breaking child components\n- **Type safety**: TypeScript catches attempts to access unavailable fields\n\n## Fragment Registry\n\nThe fragment registry is an **alternative approach** to GraphQL Code Generator's automatic fragment inlining by name. It allows you to register fragments globally, making them available throughout your application by name reference.\n\n**Important**: GraphQL Code Generator automatically inlines fragments by name wherever they're used in your queries. Either approach is sufficient on its own—**you don't need to combine them**.\n\n### Creating a Fragment Registry\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createFragmentRegistry } from \"@apollo/client/cache\";\n\nexport const fragmentRegistry = createFragmentRegistry();\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache({\n    fragments: fragmentRegistry,\n  }),\n});\n```\n\n### Registering Fragments\n\nRegister fragments after defining them:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { fragmentRegistry } from \"./apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n  }\n`;\n\nfragmentRegistry.register(USER_FRAGMENT);\n```\n\nWith colocated fragments:\n\n```tsx\nimport { fragmentRegistry } from \"@/apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Register the fragment globally\nfragmentRegistry.register(UserCard_UserFragmentDoc);\n```\n\n### Using Registered Fragments\n\nOnce registered, fragments can be referenced by name in queries without explicit imports:\n\n```tsx\n// Fragment is available by name because it's registered\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserCard_user\n    }\n  }\n`;\n```\n\n### Approaches for Fragment Composition\n\nThere are three approaches to make child fragments available in parent queries:\n\n1. **GraphQL Code Generator inlining** (Recommended): CodeGen automatically inlines fragments by name. No manual work needed—just reference fragments by name in your queries.\n\n2. **Fragment Registry**: Manually register fragments to make them available by name. Useful for runtime scenarios where CodeGen isn't available.\n\n3. **Manual interpolation**: Explicitly import and interpolate child fragments into parent fragments:\n\n   ```typescript\n   import { CHILD_FRAGMENT } from \"./ChildComponent\";\n\n   const PARENT_FRAGMENT = gql`\n     fragment Parent_data on Data {\n       field\n       ...Child_data\n     }\n     ${CHILD_FRAGMENT}\n   `;\n   ```\n\n### Pros and Cons\n\n**GraphQL Code Generator inlining**:\n\n- ✅ Less work: Automatic, no manual registration needed\n- ❌ Larger bundle: Fragments are inlined into every query that uses them\n\n**Fragment Registry**:\n\n- ✅ Smaller bundle: Fragments are registered once, referenced by name\n- ❌ More work: Requires manual registration of each fragment\n- ❌ May cause issues with lazy-loaded modules if the module is not loaded before the query is executed\n- ✅ Best for deeply nested component trees where bundle size matters\n\n**Manual interpolation**:\n\n- ❌ Most work: Manual imports and interpolation required\n- ✅ Explicit: Clear fragment dependencies in code\n\n### Recommendation\n\nFor most applications using GraphQL Code Generator (as shown in this guide), **use the automatic inlining**—it requires no additional setup and works seamlessly. Consider the fragment registry only if bundle size becomes a concern in applications with deeply nested component trees.\n\n## TypeScript Integration\n\nApollo Client provides strong TypeScript support for fragments through GraphQL Code Generator.\n\n### Generated Types\n\nGraphQL Code Generator produces typed fragment documents:\n\n```typescript\n// Generated file: fragments.generated.ts\nexport type UserCard_UserFragment = {\n  __typename: \"User\";\n  id: string;\n  name: string;\n  email: string;\n  avatarUrl: string;\n} & { \" $fragmentName\"?: \"UserCard_UserFragment\" };\n\nexport const UserCard_UserFragmentDoc: TypedDocumentNode<\n  UserCard_UserFragment,\n  never\n>;\n```\n\n### Type-Safe Fragment Usage\n\nUse `FragmentType` to accept masked fragment data:\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // 'data' is fully typed as UserCard_UserFragment\n  return <div>{data.name}</div>;\n}\n```\n\n### Fragment Type Inference\n\nTypeScript infers types from fragment documents automatically:\n\n```tsx\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Types are inferred from the fragment\nconst { data } = useSuspenseFragment({\n  fragment: UserCard_UserFragmentDoc,\n  from: user,\n});\n\n// data.name is string\n// data.email is string\n// data.nonExistentField is a TypeScript error\n```\n\n### Parent-Child Type Safety\n\nWhen passing fragment data from parent to child:\n\n```tsx\n// Parent query\nconst { data } = useSuspenseQuery(GET_USER);\n\n// TypeScript ensures the query includes UserCard_user fragment\n// before allowing it to be passed to UserCard\n<UserCard user={data.user} />;\n```\n\n## Best Practices\n\n### Prefer Colocation Over Reuse\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment, even if multiple components currently need the same fields.\n\nSharing fragments between components just because they happen to need the same fields today creates artificial dependencies. When one component's requirements change, the shared fragment must be updated, causing all components using it to over-fetch data they don't need.\n\n```tsx\n// ✅ Good: Each component has its own fragment\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n    }\n  `;\n}\n\n// If UserCard later needs 'bio', only UserCard_user changes\n// UserListItem doesn't over-fetch 'bio'\n```\n\n```tsx\n// ❌ Avoid: Sharing a generic fragment across components\nconst COMMON_USER_FIELDS = gql`\n  fragment CommonUserFields on User {\n    id\n    name\n    email\n  }\n`;\n\n// UserCard and UserListItem both use CommonUserFields\n// When UserCard needs 'bio', adding it to CommonUserFields\n// causes UserListItem to over-fetch unnecessarily\n```\n\nThis independence allows each component to evolve its data requirements without affecting unrelated parts of your application.\n\n### One Query Per Page\n\nCompose all page data requirements into a single query at the page level:\n\n```tsx\n// ✅ Good: Single page-level query\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserHeader_user\n        ...UserPosts_user\n        ...UserFriends_user\n      }\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Multiple queries in different components\nfunction UserProfile() {\n  const { data: userData } = useQuery(GET_USER);\n  const { data: postsData } = useQuery(GET_USER_POSTS);\n  const { data: friendsData } = useQuery(GET_USER_FRIENDS);\n  // ...\n}\n```\n\n### Use Fragment-Reading Hooks in Components\n\nNon-page components should use `useFragment` or `useSuspenseFragment`:\n\n```tsx\n// ✅ Good: Component reads fragment data\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n  return <div>{data.name}</div>;\n}\n```\n\n```tsx\n// ❌ Avoid: Component uses query hook\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, { variables: { id: userId } });\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Request Only Required Fields\n\nKeep fragments minimal and only request fields the component actually uses:\n\n```tsx\n// ✅ Good: Only necessary fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Requesting unused fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n      bio\n      friends {\n        id\n        name\n      }\n      posts {\n        id\n        title\n      }\n    }\n  `;\n}\n```\n\n### Use @defer for Below-the-Fold Content\n\nThe `@defer` directive allows you to defer loading of non-critical fields, enabling faster initial page loads by prioritizing essential data. The deferred fields are delivered via incremental delivery and arrive after the non-deferred data, allowing the UI to progressively render as data becomes available.\n\nDefer slow fields that aren't immediately visible:\n\n```tsx\nif (false) {\n  gql`\n    query ProductPage($id: ID!) {\n      product(id: $id) {\n        id\n        name\n        price\n        ...ProductReviews_product @defer\n      }\n    }\n  `;\n}\n```\n\nThis allows the page to render quickly while reviews load in the background.\n\n### Handle Client-Only Fields\n\nUse the `@client` directive for fields resolved locally:\n\n```tsx\nif (false) {\n  gql`\n    fragment TodoItem_todo on Todo {\n      id\n      text\n      completed\n      isSelected @client\n    }\n  `;\n}\n```\n\n### Enable Data Masking for New Applications\n\nAlways enable data masking in new applications:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true,\n});\n```\n\nThis enforces proper boundaries from the start and prevents accidental coupling between components.\n\n## Apollo Client Data Masking vs GraphQL-Codegen Fragment Masking\n\nApollo Client's data masking and GraphQL Code Generator's fragment masking are different features that serve different purposes:\n\n### GraphQL-Codegen Fragment Masking\n\nGraphQL Code Generator's fragment masking (when using the client preset) is a **type-level** feature:\n\n- Masks data only at the TypeScript type level\n- The actual runtime data remains fully accessible on the object\n- Using their `useFragment` hook simply \"unmasks\" the data on a type level\n- Does not prevent accidental access to data at runtime\n- Parent components receive all data and pass it down\n- This means the parent component has to be subscribed to all data\n\n### Apollo Client Data Masking\n\nApollo Client's data masking is a **runtime** feature with significant performance benefits:\n\n- Removes data at the runtime level, not just the type level\n- The `useFragment` and `useSuspenseFragment` hooks create cache subscriptions\n- Parent objects are sparse and only contain unmasked data\n- Prevents accidental access to data that should be masked\n\n### Key Benefits of Apollo Client Data Masking\n\n**1. No Accidental Data Access**\n\nWith runtime data masking, masked fields are not present in the parent object at all. You cannot accidentally access them, even if you bypass TypeScript type checking.\n\n**2. Fewer Re-renders**\n\nApollo Client's approach creates more efficient subscriptions:\n\n- **Without data masking**: Parent component subscribes to all fields (including masked ones). When a masked child field changes, the parent re-renders to pass that runtime data down the tree.\n- **With data masking**: Parent component only subscribes to its own unmasked fields. Subscriptions on masked fields happen lower in the React component tree when the child component calls `useSuspenseFragment`. When a masked field changes, only the child component that subscribed to it re-renders.\n\n### Example\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseQuery, useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction ParentComponent() {\n  const { data } = useSuspenseQuery(GET_USER);\n\n  // With Apollo Client data masking:\n  // - data.user only contains unmasked fields\n  // - Parent doesn't re-render when child-specific fields change\n\n  return <UserCard user={data.user} />;\n}\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a cache subscription specifically for UserCard_user fields\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // Only this component re-renders when these fields change\n  return <div>{data.name}</div>;\n}\n```\n\nThis granular subscription approach improves performance in large applications with deeply nested component trees.\n","docs/agent-skills/apollo-client/references/integration-client.md":"# Apollo Client Integration for Client-Side Apps\n\nThis guide covers setting up Apollo Client in client-side React applications without server-side rendering (SSR). This includes applications using Vite, Parcel, Create React App, or other bundlers that don't implement SSR.\n\nFor applications with SSR, use one of the framework-specific integration guides instead:\n\n- [Next.js App Router](integration-nextjs.md)\n- [React Router Framework Mode](integration-react-router.md)\n- [TanStack Start](integration-tanstack-start.md)\n\n## Installation\n\n```bash\nnpm install @apollo/client graphql rxjs\n```\n\n## TypeScript Code Generation (optional but recommended)\n\nFor type-safe GraphQL operations with TypeScript, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup Steps\n\n### Step 1: Create Client\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\n\n// Recommended: Use HttpOnly cookies for authentication\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  credentials: \"include\", // Sends cookies with requests (secure when using HttpOnly cookies)\n});\n\nconst client = new ApolloClient({\n  link: httpLink,\n  cache: new InMemoryCache(),\n});\n```\n\nIf you need manual token management (less secure, only when HttpOnly cookies aren't available):\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\n\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n});\n\nconst authLink = new SetContextLink(({ headers }) => {\n  const token = localStorage.getItem(\"token\");\n  return {\n    headers: {\n      ...headers,\n      authorization: token ? `Bearer ${token}` : \"\",\n    },\n  };\n});\n\nconst client = new ApolloClient({\n  link: authLink.concat(httpLink),\n  cache: new InMemoryCache(),\n});\n```\n\n### Step 2: Setup Provider\n\n```tsx\nimport { ApolloProvider } from \"@apollo/client\";\nimport App from \"./App\";\n\nfunction Root() {\n  return (\n    <ApolloProvider client={client}>\n      <App />\n    </ApolloProvider>\n  );\n}\n```\n\n### Step 3: Execute Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USERS = gql`\n  query GetUsers {\n    users {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserList() {\n  const { loading, error, data, dataState } = useQuery(GET_USERS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Basic Query Usage\n\n### Using Variables\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n> **Note for TypeScript users**: Use [`dataState`](https://www.apollographql.com/docs/react/data/typescript#type-narrowing-data-with-datastate) for more robust type safety and better type narrowing in Apollo Client 4.x.\n\n### TypeScript Integration\n\nFor complete examples with loading, error handling, and `dataState` for type narrowing, see [Basic Query Usage](#basic-query-usage) above.\n\n#### Usage with Generated Types\n\nFor type-safe operations with code generation, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\nQuick example:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\n// Define your query with the if (false) pattern for code generation\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n#### Usage with Manual Type Annotations\n\nIf not using code generation, define types alongside your queries using `TypedDocumentNode`:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetUserData {\n  user: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface GetUserVariables {\n  id: string;\n}\n\n// Types should always be defined via TypedDocumentNode alongside your queries/mutations, not at the useQuery/useMutation call site\nconst GET_USER: TypedDocumentNode<GetUserData, GetUserVariables> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // data.user is automatically typed from GET_USER\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Basic Mutation Usage\n\n```tsx\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\ninterface CreateUserMutation {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserMutationVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<\n  CreateUserMutation,\n  CreateUserMutationVariables\n> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (formData: FormData) => {\n    const { data } = await createUser({\n      variables: {\n        input: {\n          name: formData.get(\"name\") as string,\n          email: formData.get(\"email\") as string,\n        },\n      },\n    });\n    if (data) {\n      console.log(\"Created user:\", data.createUser);\n    }\n  };\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <input name=\"name\" placeholder=\"Name\" />\n      <input name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create User\"}\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n## Client Configuration Options\n\n```typescript\nconst client = new ApolloClient({\n  // Required: The cache implementation\n  cache: new InMemoryCache({\n    typePolicies: {\n      Query: {\n        fields: {\n          // Field-level cache configuration\n        },\n      },\n    },\n  }),\n\n  // Network layer\n  link: new HttpLink({ uri: \"/graphql\" }),\n\n  // Avoid defaultOptions if possible as they break TypeScript expectations.\n  // Configure options per-query/mutation instead for better type safety.\n  // defaultOptions: {\n  //   watchQuery: { fetchPolicy: 'cache-and-network' },\n  // },\n\n  // DevTools are enabled by default in development\n  // Only configure when enabling in production\n  devtools: {\n    enabled: true, // Only needed for production\n  },\n\n  // Custom name for this client instance\n  clientAwareness: {\n    name: \"web-client\",\n    version: \"1.0.0\",\n  },\n});\n```\n\n## Important Considerations\n\n1. **Choose Your Hook Strategy:** Decide if your application should be based on Suspense. If it is, use suspenseful hooks like `useSuspenseQuery` (see [Suspense Hooks guide](suspense-hooks.md)), otherwise use non-suspenseful hooks like `useQuery` (see [Queries guide](queries.md)).\n\n2. **Client-Side Only:** This setup is for client-side apps without SSR. The Apollo Client instance is created once and reused throughout the application lifecycle.\n\n3. **Authentication:** Prefer HttpOnly cookies with `credentials: \"include\"` in `HttpLink` options to avoid exposing tokens to JavaScript. If manual token management is necessary, use `SetContextLink` to dynamically add authentication headers from `localStorage` or other client-side storage.\n\n4. **Environment Variables:** Store your GraphQL endpoint URL in environment variables for different environments (development, staging, production).\n\n5. **Error Handling:** Always handle `loading` and `error` states when using `useQuery` or `useLazyQuery`. For Suspense-based hooks (`useSuspenseQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n","docs/agent-skills/apollo-client/references/integration-nextjs.md":"# Apollo Client Integration with Next.js App Router\n\nThis guide covers integrating Apollo Client in a Next.js application using the App Router architecture with support for both React Server Components (RSC) and Client Components.\n\n## What is supported?\n\n### React Server Components\n\nApollo Client provides a shared client instance across all server components for a single request, preventing duplicate GraphQL requests and optimizing server-side rendering.\n\n### React Client Components\n\nWhen using the `app` directory, client components are rendered both on the server (SSR) and in the browser. Apollo Client enables you to execute GraphQL queries on the server and use the results to hydrate your browser-side cache, delivering fully-rendered pages to users.\n\n## Installation\n\nInstall Apollo Client and the Next.js integration package:\n\n```bash\nnpm install @apollo/client@latest @apollo/client-integration-nextjs graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup for React Server Components (RSC)\n\n### Step 1: Create Apollo Client Configuration\n\nCreate an `ApolloClient.ts` file in your app directory:\n\n```typescript\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  registerApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nexport const { getClient, query, PreloadQuery } = registerApolloClient(() => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({\n      // Use an absolute URL for SSR (relative URLs cannot be used in SSR)\n      uri: \"https://your-api.com/graphql\",\n      fetchOptions: {\n        // Optional: Next.js-specific fetch options for caching and revalidation\n        // See: https://nextjs.org/docs/app/api-reference/functions/fetch\n      },\n    }),\n  });\n});\n```\n\n### Step 2: Use in Server Components\n\nYou can now use the `getClient` function or the `query` shortcut in your server components:\n\n```typescript\nimport { query } from \"./ApolloClient\";\n\nasync function UserProfile({ userId }: { userId: string }) {\n  const { data } = await query({\n    query: GET_USER,\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Override Next.js Fetch Options\n\nYou can override Next.js-specific `fetch` options per query using `context.fetchOptions`:\n\n```typescript\nconst { data } = await getClient().query({\n  query: GET_USER,\n  context: {\n    fetchOptions: {\n      next: { revalidate: 60 }, // Revalidate every 60 seconds\n    },\n  },\n});\n```\n\n## Setup for Client Components (SSR and Browser)\n\n### Step 1: Create Apollo Wrapper Component\n\nCreate `app/ApolloWrapper.tsx`:\n\n```typescript\n\"use client\";\n\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  ApolloNextAppProvider,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nfunction makeClient() {\n  const httpLink = new HttpLink({\n    // Use an absolute URL for SSR\n    uri: \"https://your-api.com/graphql\",\n    fetchOptions: {\n      // Optional: Next.js-specific fetch options\n      // Note: This doesn't work with `export const dynamic = \"force-static\"`\n    },\n  });\n\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: httpLink,\n  });\n}\n\nexport function ApolloWrapper({ children }: React.PropsWithChildren) {\n  return (\n    <ApolloNextAppProvider makeClient={makeClient}>\n      {children}\n    </ApolloNextAppProvider>\n  );\n}\n```\n\n### Step 2: Wrap Root Layout\n\nWrap your `RootLayout` in the `ApolloWrapper` component in `app/layout.tsx`:\n\n```typescript\nimport { ApolloWrapper } from \"./ApolloWrapper\";\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <ApolloWrapper>{children}</ApolloWrapper>\n      </body>\n    </html>\n  );\n}\n```\n\n> **Note:** This works even if your layout is a React Server Component. It ensures all Client Components share the same Apollo Client instance through `ApolloNextAppProvider`.\n\n### Step 3: Use Apollo Client Hooks in Client Components\n\nFor optimal streaming SSR, use suspense-enabled hooks like `useSuspenseQuery` and `useFragment`:\n\n```typescript\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Preloading Data from RSC to Client Components\n\nYou can preload data in React Server Components to populate the cache of your Client Components.\n\n### Step 1: Use PreloadQuery in Server Components\n\n```tsx\nimport { PreloadQuery } from \"./ApolloClient\";\nimport { Suspense } from \"react\";\n\nexport default async function Page() {\n  return (\n    <PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n      <Suspense fallback={<>Loading...</>}>\n        <ClientChild />\n      </Suspense>\n    </PreloadQuery>\n  );\n}\n```\n\n### Step 2: Consume with useSuspenseQuery in Client Components\n\n```tsx\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function ClientChild() {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n> **Important:** Data fetched this way should be considered client data and never referenced in Server Components. `PreloadQuery` prevents mixing server data and client data by creating a separate `ApolloClient` instance.\n\n### Using with useReadQuery\n\nFor advanced use cases, you can use `PreloadQuery` with `useReadQuery` to avoid request waterfalls:\n\n```tsx\n<PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n  {(queryRef) => (\n    <Suspense fallback={<>Loading...</>}>\n      <ClientChild queryRef={queryRef} />\n    </Suspense>\n  )}\n</PreloadQuery>\n```\n\nIn your Client Component:\n\n```tsx\n\"use client\";\n\nimport {\n  useQueryRefHandlers,\n  useReadQuery,\n  QueryRef,\n} from \"@apollo/client/react\";\n\nexport function ClientChild({ queryRef }: { queryRef: QueryRef<TQueryData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Handling Multipart Responses (@defer) in SSR\n\nWhen using the `@defer` directive, `useSuspenseQuery` will only suspend until the initial response is received. To handle deferred data properly, you have three strategies:\n\n### Strategy 1: Use PreloadQuery with useReadQuery\n\n`PreloadQuery` allows deferred data to be fully transported and streamed chunk-by-chunk.\n\n### Strategy 2: Remove @defer Fragments\n\nUse `RemoveMultipartDirectivesLink` to strip `@defer` directives from queries during SSR:\n\n```typescript\nimport { RemoveMultipartDirectivesLink } from \"@apollo/client-integration-nextjs\";\n\nnew RemoveMultipartDirectivesLink({\n  stripDefer: true, // Default: true\n});\n```\n\nYou can exclude specific fragments from stripping by labeling them:\n\n```graphql\nquery myQuery {\n  fastField\n  ... @defer(label: \"SsrDontStrip1\") {\n    slowField1\n  }\n}\n```\n\n### Strategy 3: Wait for Deferred Data\n\nUse `AccumulateMultipartResponsesLink` to debounce the initial response:\n\n```typescript\nimport { AccumulateMultipartResponsesLink } from \"@apollo/client-integration-nextjs\";\n\nnew AccumulateMultipartResponsesLink({\n  cutoffDelay: 100, // Wait up to 100ms for incremental data\n});\n```\n\n### Combined Approach: SSRMultipartLink\n\nCombine both strategies with `SSRMultipartLink`:\n\n```typescript\nimport { SSRMultipartLink } from \"@apollo/client-integration-nextjs\";\n\nnew SSRMultipartLink({\n  stripDefer: true,\n  cutoffDelay: 100,\n});\n```\n\n## Testing\n\nReset singleton instances between tests using the `resetApolloClientSingletons` helper:\n\n```typescript\nimport { resetApolloClientSingletons } from \"@apollo/client-integration-nextjs\";\n\nafterEach(resetApolloClientSingletons);\n```\n\n## Debugging\n\nEnable verbose logging in your `app/ApolloWrapper.tsx`:\n\n```typescript\nimport { setLogVerbosity } from \"@apollo/client\";\n\nsetLogVerbosity(\"debug\");\n```\n\n## Important Considerations\n\n1. **Separate RSC and SSR Queries:** Avoid overlapping queries between RSC and SSR. RSC queries don't update in the browser, while SSR queries can update dynamically as the cache changes.\n\n2. **Use Absolute URLs:** Always use absolute URLs in `HttpLink` for SSR, as relative URLs cannot be used in server-side rendering.\n\n3. **Streaming SSR:** For optimal performance, use `useSuspenseQuery` and `useFragment` to take advantage of React 18's streaming SSR capabilities.\n\n4. **Suspense Boundaries:** Place `Suspense` boundaries at meaningful places in your UI for the best user experience.\n","docs/agent-skills/apollo-client/references/integration-react-router.md":"# Apollo Client Integration with React Router Framework Mode\n\nThis guide covers integrating Apollo Client in a React Router 7 application with support for modern streaming SSR.\n\n## Installation\n\nInstall Apollo Client and the React Router integration package:\n\n```bash\nnpm install @apollo/client-integration-react-router @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Create Apollo Configuration\n\nCreate an `app/apollo.ts` file that exports a `makeClient` function and an `apolloLoader`:\n\n```typescript\nimport { HttpLink, InMemoryCache } from \"@apollo/client\";\nimport {\n  createApolloLoaderHandler,\n  ApolloClient,\n} from \"@apollo/client-integration-react-router\";\n\n// `request` will be available on the server during SSR or in loaders, but not in the browser\nexport const makeClient = (request?: Request) => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n};\n\nexport const apolloLoader = createApolloLoaderHandler(makeClient);\n```\n\n> **Important:** `ApolloClient` must be imported from `@apollo/client-integration-react-router`, not from `@apollo/client`.\n\n### Step 2: Reveal Entry Files\n\nRun the following command to create the entry files if they don't exist:\n\n```bash\nnpx react-router reveal\n```\n\nThis will create `app/entry.client.tsx` and `app/entry.server.tsx`.\n\n### Step 3: Configure Client Entry\n\nAdjust `app/entry.client.tsx` to wrap your app in `ApolloProvider`:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\nimport { StrictMode, startTransition } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nstartTransition(() => {\n  const client = makeClient();\n  hydrateRoot(\n    document,\n    <StrictMode>\n      <ApolloProvider client={client}>\n        <HydratedRouter />\n      </ApolloProvider>\n    </StrictMode>\n  );\n});\n```\n\n### Step 4: Configure Server Entry\n\nAdjust `app/entry.server.tsx` to wrap your app in `ApolloProvider` during SSR:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\n// ... other imports\n\nexport default function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  routerContext: EntryContext\n) {\n  return new Promise((resolve, reject) => {\n    // ... existing code\n\n    const client = makeClient(request);\n\n    const { pipe, abort } = renderToPipeableStream(\n      <ApolloProvider client={client}>\n        <ServerRouter\n          context={routerContext}\n          url={request.url}\n          abortDelay={ABORT_DELAY}\n        />\n      </ApolloProvider>,\n      {\n        [readyOption]() {\n          shellRendered = true;\n          // ... rest of the handler\n        },\n        // ... other options\n      }\n    );\n  });\n}\n```\n\n### Step 5: Add Hydration Helper\n\nAdd `<ApolloHydrationHelper>` to `app/root.tsx`:\n\n```typescript\nimport { ApolloHydrationHelper } from \"@apollo/client-integration-react-router\";\nimport { Links, Meta, Outlet, Scripts, ScrollRestoration } from \"react-router\";\n\nexport function Layout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <Meta />\n        <Links />\n      </head>\n      <body>\n        <ApolloHydrationHelper>{children}</ApolloHydrationHelper>\n        <ScrollRestoration />\n        <Scripts />\n      </body>\n    </html>\n  );\n}\n\nexport default function App() {\n  return <Outlet />;\n}\n```\n\n## Usage\n\n### Using apolloLoader with useReadQuery\n\nYou can now use the `apolloLoader` function to create Apollo-enabled loaders for your routes:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef } = useLoaderData<typeof loader>();\n  const { data } = useReadQuery(userQueryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n> **Important:** To provide better TypeScript support, `apolloLoader` is a method that you need to call twice: `apolloLoader<LoaderArgs>()(loader)`\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const postsQueryRef = preloadQuery(GET_POSTS, {\n    variables: { userId: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n    postsQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef, postsQueryRef } = useLoaderData<typeof loader>();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: postsData } = useReadQuery(postsQueryRef);\n\n  return (\n    <div>\n      <h1>{userData.user.name}</h1>\n      <h2>Posts</h2>\n      <ul>\n        {postsData.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import ApolloClient from Integration Package:** Always import `ApolloClient` from `@apollo/client-integration-react-router`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **TypeScript Support:** The `apolloLoader` function requires double invocation for proper TypeScript type inference: `apolloLoader<LoaderArgs>()(loader)`.\n\n3. **Request Context:** The `makeClient` function receives the `Request` object during SSR and in loaders, but not in the browser. Use this to set up auth headers or other request-specific configuration.\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Hydration:** The `ApolloHydrationHelper` component ensures that data loaded on the server is properly hydrated on the client, preventing unnecessary refetches.\n","docs/agent-skills/apollo-client/references/integration-tanstack-start.md":"# Apollo Client Integration with TanStack Start\n\nThis guide covers integrating Apollo Client in a TanStack Start application with support for modern streaming SSR.\n\n> **Note:** When using `npx create-tsrouter-app` to create a new TanStack Start application, you can choose Apollo Client in the setup wizard to have all of this configuration automatically set up for you.\n\n## Installation\n\nInstall Apollo Client and the TanStack Start integration package:\n\n```bash\nnpm install @apollo/client-integration-tanstack-start @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Configure Root Route with Context\n\nIn your `routes/__root.tsx`, change from `createRootRoute` to `createRootRouteWithContext` to provide the right context type:\n\n```typescript\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\nimport {\n  createRootRouteWithContext,\n  Outlet,\n} from \"@tanstack/react-router\";\n\nexport const Route = createRootRouteWithContext<ApolloClientIntegration.RouterContext>()({\n  component: RootComponent,\n});\n\nfunction RootComponent() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Outlet />\n      </body>\n    </html>\n  );\n}\n```\n\n### Step 2: Set Up Apollo Client in Router\n\nIn your `router.tsx`, set up your Apollo Client instance and run `routerWithApolloClient`:\n\n```typescript\nimport {\n  routerWithApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** `ApolloClient` and `InMemoryCache` must be imported from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`.\n\n## Usage\n\n### Option 1: Loader with preloadQuery and useReadQuery\n\nUse the `preloadQuery` function in your route loader to preload data during navigation:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/user/$userId\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery }, params }) => {\n    const queryRef = preloadQuery(GET_USER, {\n      variables: { id: params.userId },\n    });\n\n    return {\n      queryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { queryRef } = Route.useLoaderData();\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Option 2: Direct useSuspenseQuery in Component\n\nYou can also use Apollo Client's suspenseful hooks directly in your component without a loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_POSTS: TypedDocumentNode<{\n  posts: Array<{ id: string; title: string; content: string }>;\n}> = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n      content\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/posts\")({\n  component: RouteComponent,\n});\n\nfunction RouteComponent() {\n  const { data } = useSuspenseQuery(GET_POSTS);\n\n  return (\n    <div>\n      <h1>Posts</h1>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>\n            <h2>{post.title}</h2>\n            <p>{post.content}</p>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const Route = createFileRoute(\"/dashboard\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery } }) => {\n    const userQueryRef = preloadQuery(GET_USER, {\n      variables: { id: \"current\" },\n    });\n\n    const statsQueryRef = preloadQuery(GET_STATS, {\n      variables: { period: \"month\" },\n    });\n\n    return {\n      userQueryRef,\n      statsQueryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { userQueryRef, statsQueryRef } = Route.useLoaderData();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: statsData } = useReadQuery(statsQueryRef);\n\n  return (\n    <div>\n      <h1>Welcome, {userData.user.name}</h1>\n      <div>\n        <h2>Monthly Stats</h2>\n        <p>Views: {statsData.stats.views}</p>\n        <p>Clicks: {statsData.stats.clicks}</p>\n      </div>\n    </div>\n  );\n}\n```\n\n### Using useQueryRefHandlers for Refetching\n\nWhen using `useReadQuery`, you can get refetch functionality from `useQueryRefHandlers`:\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n```typescript\nimport { useReadQuery, useQueryRefHandlers, QueryRef } from \"@apollo/client/react\";\n\nfunction UserComponent({ queryRef }: { queryRef: QueryRef<GetUserQuery> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <button onClick={() => refetch()}>Refresh</button>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import from Integration Package:** Always import `ApolloClient` and `InMemoryCache` from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **Context Type:** Use `createRootRouteWithContext<ApolloClientIntegration.RouterContext>()` to provide proper TypeScript types for the `preloadQuery` function in loaders.\n\n3. **Loader vs Component Queries:**\n\n   - Use `preloadQuery` in loaders when you want to start fetching data before the component renders\n   - Use `useSuspenseQuery` directly in components for simpler cases or when data fetching can wait until render\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Management:** The Apollo Client instance is shared across all routes, so cache updates from one route will be reflected in all routes that use the same data.\n\n6. **Authentication:** Use Apollo Client's `SetContextLink` for dynamic auth tokens.\n\n## Advanced Configuration\n\n### Adding Authentication\n\nFor authentication in TanStack Start with SSR support, you need to handle both server and client environments differently. Use `createIsomorphicFn` to provide environment-specific implementations:\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n  routerWithApolloClient,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { ApolloLink, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\nimport { createIsomorphicFn } from \"@tanstack/react-start\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { getSession, getCookie } from \"@tanstack/react-start/server\";\nimport { routeTree } from \"./routeTree.gen\";\n\n// Create isomorphic link that uses different implementations per environment\nconst createAuthLink = createIsomorphicFn()\n  .server(() => {\n    // Server-only: Can access server-side functions like `getCookies`, `getCookie`, `getSession`, etc. exported from `\"@tanstack/react-start/server\"`\n    return new SetContextLink(async (prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: getCookie(\"Authorization\"),\n        },\n      };\n    });\n  })\n  .client(() => {\n    // Client-only: Can access `localStorage` or other browser APIs\n    return new SetContextLink((prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: localStorage.getItem(\"authToken\") ?? \"\",\n        },\n      };\n    });\n  });\n\nexport function getRouter() {\n  const httpLink = new HttpLink({\n    uri: \"https://your-graphql-endpoint.com/graphql\",\n  });\n\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: ApolloLink.from([createAuthLink(), httpLink]),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** The `getRouter` function is called both on the server and client, so it must not contain environment-specific code. Use `createIsomorphicFn` to provide different implementations:\n>\n> - **Server:** Can access server-only functions like `getSession`, `getCookies`, `getCookie` from `@tanstack/react-start/server` to access authentication information in request or session data\n> - **Client:** Can use `localStorage` or other browser APIs to access auth tokens (if setting `credentials: \"include\"` is sufficient, try to prefer that over manually setting auth headers client-side)\n>\n> This ensures your authentication works correctly in both SSR and browser contexts.\n\n### Custom Cache Configuration\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\nimport { routerWithApolloClient } from \"@apollo/client-integration-tanstack-start\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache({\n      typePolicies: {\n        Query: {\n          fields: {\n            posts: {\n              merge(existing = [], incoming) {\n                return [...existing, ...incoming];\n              },\n            },\n          },\n        },\n      },\n    }),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n","docs/agent-skills/apollo-client/references/mutations.md":"# Mutations Reference\n\n## Table of Contents\n\n- [useMutation Hook](#usemutation-hook)\n- [Mutation Variables](#mutation-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [Optimistic UI](#optimistic-ui)\n- [Cache Updates](#cache-updates)\n- [Refetch Queries](#refetch-queries)\n- [Error Handling](#error-handling)\n\n## useMutation Hook\n\nThe `useMutation` hook is used to execute GraphQL mutations.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst ADD_TODO = gql`\n  mutation AddTodo($text: String!) {\n    addTodo(text: $text) {\n      id\n      text\n      completed\n    }\n  }\n`;\n\nfunction AddTodo() {\n  const [addTodo, { data, loading, error }] = useMutation(ADD_TODO);\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        const form = e.currentTarget;\n        const text = new FormData(form).get(\"text\") as string;\n        addTodo({ variables: { text } });\n        form.reset();\n      }}\n    >\n      <input name=\"text\" placeholder=\"Add todo\" />\n      <button type=\"submit\" disabled={loading}>\n        Add\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n### Return Tuple\n\n```typescript\nconst [\n  mutateFunction, // Function to call to execute mutation\n  {\n    data, // Mutation result data\n    loading, // True while mutation is in flight\n    error, // ApolloError if mutation failed\n    called, // True if mutation has been called\n    reset, // Reset mutation state\n    client, // Apollo Client instance\n  },\n] = useMutation(MUTATION);\n```\n\n## Mutation Variables\n\n### Variables in Options\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  variables: {\n    input: {\n      name: \"Default User\",\n      email: \"default@example.com\",\n    },\n  },\n});\n\n// Call with default variables\nawait createUser();\n\n// Override variables\nawait createUser({\n  variables: {\n    input: {\n      name: \"Custom User\",\n      email: \"custom@example.com\",\n    },\n  },\n});\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\n\ninterface CreateUserData {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<CreateUserData, CreateUserVariables> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nconst [createUser, { data, loading }] = useMutation(CREATE_USER);\n\nconst { data } = await createUser({\n  variables: {\n    input: { name: \"John\", email: \"john@example.com\" },\n  },\n});\n\n// data.createUser is fully typed\n```\n\n## Loading and Error States\n\n### Handling in UI\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading, error, data, reset }] =\n    useMutation(CREATE_POST);\n\n  if (data) {\n    return (\n      <div>\n        <p>Post created: {data.createPost.title}</p>\n        <button onClick={reset}>Create another</button>\n      </div>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <input name=\"title\" disabled={loading} />\n      <textarea name=\"content\" disabled={loading} />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n      {error && (\n        <div className=\"error\">\n          <p>Failed to create post: {error.message}</p>\n          <button onClick={reset}>Try again</button>\n        </div>\n      )}\n    </form>\n  );\n}\n```\n\n### Async/Await Pattern\n\nIf you only need the promise without using the hook's loading/data state, use `client.mutate` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction CreatePost() {\n  const client = useApolloClient();\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await client.mutate({\n        mutation: CREATE_POST,\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      ...\n    </form>\n  );\n}\n```\n\nIf you do use the hook's state, e.g. because you want to render the `loading` state, errors or returned `data`, you can also use the `useMutation` hook with `async..await` in your handler:\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading }] = useMutation(CREATE_POST);\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await createPost({\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n    </form>\n  );\n}\n```\n\n## Optimistic UI\n\nOptimistic UI immediately reflects the expected result of a mutation before the server responds.\n\n### Basic Optimistic Response\n\n**Important**: `optimisticResponse` needs to be a full valid response for the mutation. A partial result might result in subtle errors.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  optimisticResponse: {\n    addTodo: {\n      __typename: \"Todo\",\n      id: \"temp-id\",\n      text: \"New todo\",\n      completed: false,\n    },\n  },\n});\n```\n\n### Dynamic Optimistic Response\n\n```tsx\nfunction TodoList() {\n  const [addTodo] = useMutation(ADD_TODO);\n\n  const handleAdd = (text: string) => {\n    addTodo({\n      variables: { text },\n      optimisticResponse: {\n        addTodo: {\n          __typename: \"Todo\",\n          id: `temp-${Date.now()}`,\n          text,\n          completed: false,\n        },\n      },\n    });\n  };\n\n  return <AddTodoForm onAdd={handleAdd} />;\n}\n```\n\n### Optimistic Response with Cache Update\n\n```tsx\nconst [toggleTodo] = useMutation(TOGGLE_TODO, {\n  optimisticResponse: ({ id }) => ({\n    toggleTodo: {\n      __typename: \"Todo\",\n      id,\n      completed: true, // Assume success\n    },\n  }),\n  update: (cache, { data }) => {\n    // This runs twice: once with optimistic data, once with server data\n    cache.modify({\n      id: cache.identify(data.toggleTodo),\n      fields: {\n        completed: () => data.toggleTodo.completed,\n      },\n    });\n  },\n});\n```\n\n## Cache Updates\n\n### Using update Function\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Read existing todos from cache\n    const existingTodos = cache.readQuery<{ todos: Todo[] }>({\n      query: GET_TODOS,\n    });\n\n    // Write updated list back to cache\n    cache.writeQuery({\n      query: GET_TODOS,\n      data: {\n        todos: [...(existingTodos?.todos ?? []), data.addTodo],\n      },\n    });\n  },\n});\n```\n\n### cache.modify\n\n```tsx\nconst [deleteTodo] = useMutation(DELETE_TODO, {\n  update: (cache, { data }) => {\n    cache.modify({\n      fields: {\n        todos: (existingTodos: Reference[], { readField }) => {\n          return existingTodos.filter(\n            (todoRef) => readField(\"id\", todoRef) !== data.deleteTodo.id\n          );\n        },\n      },\n    });\n  },\n});\n```\n\n### cache.evict\n\n```tsx\nconst [deleteUser] = useMutation(DELETE_USER, {\n  update: (cache, { data }) => {\n    // Remove the user object from cache entirely\n    cache.evict({ id: cache.identify(data.deleteUser) });\n    // Clean up dangling references\n    cache.gc();\n  },\n});\n```\n\n### Updating Related Queries\n\n```tsx\nconst [createPost] = useMutation(CREATE_POST, {\n  update: (cache, { data }) => {\n    // Update author's post count\n    cache.modify({\n      id: cache.identify({ __typename: \"User\", id: data.createPost.authorId }),\n      fields: {\n        postCount: (existing) => existing + 1,\n        posts: (existing, { toReference }) => [\n          ...existing,\n          toReference(data.createPost),\n        ],\n      },\n    });\n\n    // Add to feed\n    cache.modify({\n      fields: {\n        feed: (existing, { toReference }) => [\n          toReference(data.createPost),\n          ...existing,\n        ],\n      },\n    });\n  },\n});\n```\n\n## Refetch Queries\n\n### Basic Refetch\n\nThere are three refetch notations:\n\n- **String**: `refetchQueries: ['getTodos']` - refetches all active `getTodos` queries\n- **Query document**: `refetchQueries: [GET_TODOS]` - refetches all active queries using this document\n- **Object**: `refetchQueries: [{ query: GET_TODOS }, { query: GET_TODOS, variables: { page: 25 } }]` - **fetches** the query, regardless if it's actively used in the UI\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  // Refetch all active GET_TODOS queries\n  refetchQueries: [\"getTodos\"],\n  // Or: refetchQueries: [GET_TODOS],\n});\n\n// Fetch specific query with variables (even if not active)\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }, { query: GET_TODO_COUNT }],\n});\n```\n\n### Conditional Refetch\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: (result) => {\n    if (result.data?.addTodo.priority === \"HIGH\") {\n      return [{ query: GET_HIGH_PRIORITY_TODOS }];\n    }\n    return [{ query: GET_TODOS }];\n  },\n});\n```\n\n### Refetch Active Queries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: \"active\", // Refetch all active queries\n  // Or: 'all' to refetch all queries (including inactive)\n});\n```\n\n### awaitRefetchQueries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }],\n  awaitRefetchQueries: true, // Wait for refetch before resolving mutation\n});\n```\n\n### onQueryUpdated\n\nReturning `true` from `onQueryUpdated` causes a refetch. Don't call `refetch()` manually inside `onQueryUpdated`, as it won't retain the query and might cancel it early.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Update cache...\n  },\n  onQueryUpdated: (observableQuery) => {\n    // Called for each query affected by cache update\n    console.log(`Query ${observableQuery.queryName} was updated`);\n    // Return true to refetch\n    return true;\n  },\n});\n```\n\n## Error Handling\n\n### Error Policy\n\n```tsx\nconst [createUser, { loading }] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\", // Return both data and errors\n});\n\nconst { data, errors } = await createUser({\n  variables: { input },\n});\n\n// Handle partial success\nif (data?.createUser) {\n  console.log(\"User created:\", data.createUser);\n}\nif (errors) {\n  console.warn(\"Some errors occurred:\", errors);\n}\n```\n\n### onError Callback\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  onError: (error) => {\n    // Handle error globally\n    toast.error(`Failed to create user: ${error.message}`);\n\n    // Log to error tracking service\n    Sentry.captureException(error);\n  },\n  onCompleted: (data) => {\n    toast.success(`User ${data.createUser.name} created!`);\n  },\n});\n```\n\n### Field-Level Errors\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\",\n});\n\nconst handleSubmit = async (input: CreateUserInput) => {\n  const { data, errors } = await createUser({\n    variables: { input },\n  });\n\n  // Handle GraphQL validation errors\n  const fieldErrors = errors?.reduce(\n    (acc, error) => {\n      const field = error.extensions?.field as string;\n      if (field) {\n        acc[field] = error.message;\n      }\n      return acc;\n    },\n    {} as Record<string, string>\n  );\n\n  if (fieldErrors?.email) {\n    setEmailError(fieldErrors.email);\n  }\n};\n```\n","docs/agent-skills/apollo-client/references/queries.md":"# Queries Reference\n\n> **Note**: In most applications, there should only be one use of a query hook per page. Use fragment-reading hooks (`useFragment`, `useSuspenseFragment`) with component-colocated fragments and data masking for the rest of the page components.\n\n## Table of Contents\n\n- [useQuery Hook](#usequery-hook)\n- [Query Variables](#query-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [useLazyQuery](#uselazyquery)\n- [Polling and Refetching](#polling-and-refetching)\n- [Fetch Policies](#fetch-policies)\n- [Conditional Queries](#conditional-queries)\n\n## useQuery Hook\n\nThe `useQuery` hook is the primary way to fetch data in Apollo Client in non-suspenseful applications. It returns loading and error states that must be handled.\n\n> **Note**: In suspenseful applications, use `useSuspenseQuery` or `useBackgroundQuery` instead. See the [Suspense Hooks reference](suspense-hooks.md) for more details.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_DOGS = gql`\n  query GetDogs {\n    dogs {\n      id\n      breed\n      displayImage\n    }\n  }\n`;\n\nfunction Dogs() {\n  const { loading, error, data } = useQuery(GET_DOGS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  loading, // True during initial load\n  error, // ApolloError if request failed\n  networkStatus, // Detailed network state (1-8)\n  dataState, // For TypeScript type narrowing (AC 4.x)\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n  startPolling, // Start polling at interval\n  stopPolling, // Stop polling\n  subscribeToMore, // Add subscription to query\n  updateQuery, // Manually update query result\n  client, // Apollo Client instance\n  called, // True if query has been executed\n  previousData, // Previous data (useful during loading)\n} = useQuery(QUERY);\n```\n\n## Query Variables\n\n### Basic Variables\n\n```tsx\nconst GET_DOG = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DogPhoto({ breed }: { breed: string }) {\n  const { loading, error, data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  if (loading) return null;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <img src={data.dog.displayImage} alt={breed} />;\n}\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters for better type safety:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetDogData {\n  dog: {\n    id: string;\n    displayImage: string;\n  };\n}\n\ninterface GetDogVariables {\n  breed: string;\n}\n\nconst GET_DOG: TypedDocumentNode<GetDogData, GetDogVariables> = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_DOG, {\n  variables: { breed: \"bulldog\" },\n});\n\n// data?.dog is fully typed\n```\n\n### Dynamic Variables\n\n```tsx\nfunction DogSelector() {\n  const [breed, setBreed] = useState(\"bulldog\");\n\n  // Query automatically re-runs when breed changes\n  const { data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  return (\n    <select value={breed} onChange={(e) => setBreed(e.target.value)}>\n      <option value=\"bulldog\">Bulldog</option>\n      <option value=\"poodle\">Poodle</option>\n    </select>\n  );\n}\n```\n\n## Loading and Error States\n\n### Using Previous Data\n\n```tsx\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, data, previousData } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // Show previous data while loading new data\n  const displayData = data ?? previousData;\n\n  return (\n    <div>\n      {loading && <LoadingSpinner />}\n      {displayData && <UserCard user={displayData.user} />}\n    </div>\n  );\n}\n```\n\n### Network Status\n\n```tsx\nimport { NetworkStatus } from \"@apollo/client\";\n\nfunction Dogs() {\n  const { loading, error, data, networkStatus, refetch } = useQuery(GET_DOGS, {\n    notifyOnNetworkStatusChange: true,\n  });\n\n  if (networkStatus === NetworkStatus.refetch) {\n    return <p>Refetching...</p>;\n  }\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <>\n      <button onClick={() => refetch()}>Refresh</button>\n      <ul>\n        {data.dogs.map((dog) => (\n          <li key={dog.id}>{dog.breed}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n## useLazyQuery\n\nUse `useLazyQuery` when you want to execute a query in response to a user-triggered event (like a button click) rather than on component mount.\n\n**Important**: `useLazyQuery` doesn't guarantee a network request - it only sets variables. If data is already in the cache, this isn't a \"refetch\". Only use `useLazyQuery` if you consume the second tuple value (loading, data, error states) to synchronize cache data with the component. If you only need the promise, use `client.query` directly instead.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useLazyQuery } from \"@apollo/client/react\";\n\nconst GET_DOG_PHOTO = gql`\n  query GetDogPhoto($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DelayedQuery() {\n  const [getDog, { loading, error, data, called }] =\n    useLazyQuery(GET_DOG_PHOTO);\n\n  if (called && loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <div>\n      {data?.dog && <img src={data.dog.displayImage} />}\n      <button onClick={() => getDog({ variables: { breed: \"bulldog\" } })}>\n        Get Bulldog Photo\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use client.query Instead\n\nIf you only need the promise result and don't consume the loading/error/data states from the hook, use `client.query` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction SearchDogs() {\n  const client = useApolloClient();\n  const [search, setSearch] = useState(\"\");\n\n  const handleSearch = async () => {\n    try {\n      const { data } = await client.query({\n        query: SEARCH_DOGS,\n        variables: { query: search },\n      });\n      console.log(\"Found dogs:\", data.searchDogs);\n    } catch (error) {\n      console.error(\"Search failed:\", error);\n    }\n  };\n\n  return (\n    <div>\n      <input value={search} onChange={(e) => setSearch(e.target.value)} />\n      <button onClick={handleSearch}>Search</button>\n    </div>\n  );\n}\n```\n\n## Polling and Refetching\n\n### Polling\n\n```tsx\nfunction LiveFeed() {\n  const { data, startPolling, stopPolling } = useQuery(GET_FEED, {\n    pollInterval: 5000, // Poll every 5 seconds\n  });\n\n  // Or control polling dynamically\n  useEffect(() => {\n    startPolling(5000);\n    return () => stopPolling();\n  }, [startPolling, stopPolling]);\n\n  return <Feed items={data?.feed} />;\n}\n```\n\n### Manual Refetching\n\n```tsx\nfunction DogList() {\n  const { data, refetch } = useQuery(GET_DOGS);\n\n  return (\n    <div>\n      <button onClick={() => refetch()}>Refresh</button>\n      <button onClick={() => refetch({ breed: \"poodle\" })}>\n        Refetch Poodles\n      </button>\n      <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>\n    </div>\n  );\n}\n```\n\n## Fetch Policies\n\nControl how the query interacts with the cache.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-only`        | Only return cached data, never fetch                       |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n| `standby`           | Same as cache-first but doesn't auto-update                |\n\n### Usage Examples\n\n```tsx\n// Real-time data - always fetch\nconst { data } = useQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Static data - prefer cache\nconst { data } = useQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data, loading } = useQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n\n// Fetch once, then use cache\nconst { data } = useQuery(GET_USER_PROFILE, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n```\n\n### nextFetchPolicy\n\n```tsx\n// First request: network-only\n// Subsequent requests: cache-first\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n\n// Or use a function for more control\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: (currentFetchPolicy, { reason, observable }) => {\n    if (reason === \"after-fetch\") {\n      return \"cache-first\";\n    }\n    return currentFetchPolicy;\n  },\n});\n```\n\n## Conditional Queries\n\n### Using skipToken (Recommended)\n\nUse `skipToken` to conditionally skip queries without TypeScript issues:\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n### Skip Option (Alternative)\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId! },\n    skip: !userId, // Don't execute if no userId\n  });\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n> **Note**: Using `skipToken` is preferred over `skip` as it avoids TypeScript issues with required variables and the non-null assertion operator.\n\n### SSR Skip\n\n```tsx\n// Skip during server-side rendering\nconst { data } = useQuery(GET_USER_LOCATION, {\n  skip: typeof window === \"undefined\",\n  ssr: false,\n});\n```\n","docs/agent-skills/apollo-client/references/state-management.md":"# State Management Reference\n\n## Table of Contents\n\n- [Reactive Variables](#reactive-variables)\n- [Local-Only Fields](#local-only-fields)\n- [LocalState Resolvers](#localstate-resolvers)\n- [Combining Remote and Local State](#combining-remote-and-local-state)\n- [useReactiveVar Hook](#usereactivevar-hook)\n\n## Reactive Variables\n\nReactive variables are a way to store local state outside of the Apollo Client cache while still triggering reactive updates.\n\n**Important**: Reactive variables store a single value that notifies `ApolloClient` instances when changed. They do not have separate values per ApolloClient instance. In multi-user environments like SSR, global or module-level reactive variables could be shared between users and cause data leaks. In frameworks that use SSR, always avoid storing reactive variables as globals.\n\n### Creating Reactive Variables\n\n```typescript\nimport { makeVar } from \"@apollo/client\";\n\n// Simple reactive variable\nexport const isLoggedInVar = makeVar<boolean>(false);\n\n// Object reactive variable\nexport const cartItemsVar = makeVar<CartItem[]>([]);\n\n// Complex state\ninterface AppState {\n  theme: \"light\" | \"dark\";\n  sidebarOpen: boolean;\n  notifications: Notification[];\n}\n\nexport const appStateVar = makeVar<AppState>({\n  theme: \"light\",\n  sidebarOpen: true,\n  notifications: [],\n});\n```\n\n### Reading Reactive Variables\n\n```tsx\n// Direct read (non-reactive)\nconst isLoggedIn = isLoggedInVar();\n\n// Reactive read in component\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction AuthButton() {\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return isLoggedIn ?\n      <button onClick={() => isLoggedInVar(false)}>Logout</button>\n    : <button onClick={() => isLoggedInVar(true)}>Login</button>;\n}\n```\n\n### Updating Reactive Variables\n\n```typescript\n// Set new value\nisLoggedInVar(true);\n\n// Update based on current value\ncartItemsVar([...cartItemsVar(), newItem]);\n\n// Update object state\nappStateVar({\n  ...appStateVar(),\n  theme: \"dark\",\n});\n\n// Helper function pattern\nexport function toggleSidebar() {\n  const current = appStateVar();\n  appStateVar({ ...current, sidebarOpen: !current.sidebarOpen });\n}\n\nexport function addNotification(notification: Notification) {\n  const current = appStateVar();\n  appStateVar({\n    ...current,\n    notifications: [...current.notifications, notification],\n  });\n}\n```\n\n## Local-Only Fields\n\nLocal-only fields are fields defined in queries but resolved entirely on the client using the `@client` directive.\n\n**Important**: To use any `@client` fields, you need to add `LocalState` to the `ApolloClient` initialization:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({}),\n  // ... other options\n});\n```\n\n> **Note**: `LocalState` is an Apollo Client 4.x concept and did not exist as a class in previous versions. In previous versions, a `localState` option was not necessary, and local resolvers (if used) could be passed directly to the `ApolloClient` constructor.\n\n### Basic @client Fields\n\n```tsx\nconst GET_USER_WITH_LOCAL = gql`\n  query GetUserWithLocal($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n      # Local-only fields\n      isSelected @client\n      displayName @client\n    }\n  }\n`;\n\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER_WITH_LOCAL, {\n    variables: { id: userId },\n  });\n\n  return (\n    <div className={data?.user.isSelected ? \"selected\" : \"\"}>\n      <h2>{data?.user.displayName}</h2>\n      <p>{data?.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Local Field Read Functions (Type Policies)\n\nLocal field `read` functions are defined in entity-level type policies. You can use reactive variables inside these `read` functions, along with other calculations or derived values:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Simple local field from reactive variable\n        isSelected: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return selectedUsersVar().includes(id);\n          },\n        },\n\n        // Computed local field (derived value)\n        displayName: {\n          read(_, { readField }) {\n            const name = readField(\"name\");\n            const email = readField(\"email\");\n            return name || email?.split(\"@\")[0] || \"Anonymous\";\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## LocalState Resolvers\n\n### Query-Level Local Resolvers\n\nQuery-level local fields can be defined using `LocalState` resolvers. **Note**: Do not read reactive variables inside LocalState resolvers - this is not a documented/tested feature. It might not behave as expected.\n\n```typescript\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Query: {\n        // Read from localStorage\n        theme: () => {\n          if (typeof window !== \"undefined\") {\n            return localStorage.getItem(\"theme\") || \"light\";\n          }\n          return \"light\";\n        },\n\n        // Read from cache\n        currentUser: (_, __, { cache }) => {\n          if (typeof window === \"undefined\") return null;\n          const userId = localStorage.getItem(\"currentUserId\");\n          if (!userId) return null;\n          return cache.readFragment({\n            id: cache.identify({ __typename: \"User\", id: userId }),\n            fragment: gql`\n              fragment CurrentUser on User {\n                id\n                name\n                email\n              }\n            `,\n          });\n        },\n\n        // Compute value\n        isOnline: () => {\n          if (typeof navigator !== \"undefined\") {\n            return navigator.onLine;\n          }\n          return true;\n        },\n      },\n    },\n  }),\n});\n```\n\n### Using Local Query Fields\n\n```tsx\nconst GET_AUTH_STATE = gql`\n  query GetAuthState {\n    isLoggedIn @client\n    currentUser @client {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction AuthStatus() {\n  const { data } = useQuery(GET_AUTH_STATE);\n\n  if (!data?.isLoggedIn) {\n    return <LoginButton />;\n  }\n\n  return <UserMenu user={data.currentUser} />;\n}\n```\n\n## Combining Remote and Local State\n\n### Mixing Remote and Local Fields\n\n```tsx\nconst GET_PRODUCTS = gql`\n  query GetProducts {\n    products {\n      id\n      name\n      price\n      # Local fields\n      quantity @client\n      isInCart @client\n    }\n  }\n`;\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Product: {\n      fields: {\n        quantity: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            const cartItem = cartItemsVar().find(\n              (item) => item.productId === id\n            );\n            return cartItem?.quantity ?? 0;\n          },\n        },\n\n        isInCart: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return cartItemsVar().some((item) => item.productId === id);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Local Mutations\n\n```tsx\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Mutation: {\n        addToCart: (_, { productId, quantity }, { cache }) => {\n          // Read current cart from cache\n          const { cart } = cache.readQuery({ query: GET_CART }) || { cart: [] };\n\n          const existing = cart.find((item) => item.productId === productId);\n\n          const updatedCart =\n            existing ?\n              cart.map((item) =>\n                item.productId === productId ?\n                  { ...item, quantity: item.quantity + quantity }\n                : item\n              )\n            : [...cart, { productId, quantity, __typename: \"CartItem\" }];\n\n          // Write updated cart back to cache\n          cache.writeQuery({\n            query: GET_CART,\n            data: { cart: updatedCart },\n          });\n\n          return true;\n        },\n      },\n    },\n  }),\n});\n\nconst ADD_TO_CART = gql`\n  mutation AddToCart($productId: ID!, $quantity: Int!) {\n    addToCart(productId: $productId, quantity: $quantity) @client\n  }\n`;\n```\n\n### Persisting Local State\n\n```typescript\n// Create a helper function to permanently subscribe to reactive variable changes, without creating memory leaks\nfunction subscribeToVariable<T>(\n  weakRef: WeakRef<ReactiveVar<T>>,\n  listener: ReactiveListener<T>\n) {\n  weakRef.deref()?.onNextChange((value) => {\n    listener(value);\n    subscribeToVariable(weakRef, listener);\n  });\n}\n\n// Create reactive variable with persistence\nconst persistentCartVar = makeVar<CartItem[]>(\n  typeof window !== \"undefined\" && localStorage.getItem(\"cart\") ?\n    JSON.parse(localStorage.getItem(\"cart\")!)\n  : []\n);\n\n// Save to localStorage when reactive variable changes\nsubscribeToVariable(new WeakRef(persistentCartVar), (items) => {\n  try {\n    if (typeof window !== \"undefined\") {\n      localStorage.setItem(\"cart\", JSON.stringify(items));\n    }\n  } catch (error) {\n    console.error(\"Failed to persist cart:\", error);\n  }\n});\n```\n\n## useReactiveVar Hook\n\nThe `useReactiveVar` hook subscribes a component to reactive variable updates.\n\n### Basic Usage\n\n```tsx\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction ThemeToggle() {\n  const theme = useReactiveVar(themeVar);\n\n  return (\n    <button onClick={() => themeVar(theme === \"light\" ? \"dark\" : \"light\")}>\n      Current: {theme}\n    </button>\n  );\n}\n```\n\n### With Derived State\n\n```tsx\nfunction CartSummary() {\n  const cartItems = useReactiveVar(cartItemsVar);\n\n  // Derived values are computed on each render\n  const totalItems = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n  const totalPrice = cartItems.reduce(\n    (sum, item) => sum + item.price * item.quantity,\n    0\n  );\n\n  return (\n    <div>\n      <p>Items: {totalItems}</p>\n      <p>Total: ${totalPrice.toFixed(2)}</p>\n    </div>\n  );\n}\n```\n\n### Multiple Reactive Variables\n\n```tsx\nfunction AppLayout() {\n  const theme = useReactiveVar(themeVar);\n  const sidebarOpen = useReactiveVar(sidebarOpenVar);\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return (\n    <div className={`app ${theme}`}>\n      {isLoggedIn && sidebarOpen && <Sidebar />}\n      <main>\n        <Outlet />\n      </main>\n    </div>\n  );\n}\n```\n","docs/agent-skills/apollo-client/references/suspense-hooks.md":"# Suspense Hooks Reference\n\n> **Note**: Suspense hooks are the recommended approach for data fetching in modern React applications (React 18+). They provide cleaner code, better loading state handling, and enable streaming SSR.\n\n## Table of Contents\n\n- [useSuspenseQuery Hook](#usesuspensequery-hook)\n- [useBackgroundQuery and useReadQuery](#usebackgroundquery-and-usereadquery)\n- [useLoadableQuery](#useloadablequery)\n- [createQueryPreloader](#createquerypreloader)\n- [useQueryRefHandlers](#usequeryrefhandlers)\n- [Distinguishing Queries with queryKey](#distinguishing-queries-with-querykey)\n- [Suspense Boundaries and Error Handling](#suspense-boundaries-and-error-handling)\n- [Transitions](#transitions)\n- [Avoiding Request Waterfalls](#avoiding-request-waterfalls)\n- [Fetch Policies](#fetch-policies)\n- [Streaming SSR or React Server Components](#streaming-ssr-or-react-server-components)\n- [Conditional Queries](#conditional-queries)\n\n## useSuspenseQuery Hook\n\nThe `useSuspenseQuery` hook is the Suspense-ready replacement for `useQuery`. It initiates a network request and causes the component calling it to suspend while the request is made. Unlike `useQuery`, it does not return `loading` states—these are handled by React's Suspense boundaries and error boundaries.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { GET_DOG } from \"./queries.generated\";\n\nfunction App() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Dog id=\"3\" />\n    </Suspense>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  // data is always defined when this component renders\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  dataState, // With default options: \"complete\" | \"streaming\"\n  // With returnPartialData: also \"partial\"\n  // With errorPolicy \"all\" or \"ignore\": also \"empty\"\n  error, // ApolloError (only when errorPolicy is \"all\" or \"ignore\")\n  networkStatus, // NetworkStatus.ready, NetworkStatus.loading, etc.\n  client, // Apollo Client instance\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n} = useSuspenseQuery(QUERY, options);\n```\n\n### Key Differences from useQuery\n\n- **No `loading` boolean**: Component suspends instead of returning `loading: true`\n- **Error handling**: With default `errorPolicy` (`none`), errors are thrown and caught by error boundaries. With `errorPolicy: \"all\"` or `\"ignore\"`, the `error` property is returned and `data` may be `undefined`.\n- **`data` availability**: With default `errorPolicy` (`none`), `data` is guaranteed to be present when the component renders. With `errorPolicy: \"all\"` or `\"ignore\"`, when `dataState` is `empty`, `data` may be `undefined`.\n- **Suspense boundaries**: Must wrap component with `<Suspense>` to handle loading state\n\n### Changing Variables\n\nWhen variables change, `useSuspenseQuery` automatically re-runs the query. If the data is not in the cache, the component suspends again.\n\n```tsx\nimport { useState } from \"react\";\nimport { GET_DOGS } from \"./queries.generated\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => setSelectedDog(e.target.value)}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog.name}</div>\n      <div>Breed: {data.dog.breed}</div>\n    </>\n  );\n}\n```\n\n### Rendering Partial Data\n\nUse `returnPartialData` to render immediately with partial cache data instead of suspending. The component will still suspend if there is no data in the cache.\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    returnPartialData: true,\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog?.name ?? \"Unknown\"}</div>\n      {data.dog?.breed && <div>Breed: {data.dog.breed}</div>}\n    </>\n  );\n}\n```\n\n## useBackgroundQuery and useReadQuery\n\nUse `useBackgroundQuery` with `useReadQuery` to avoid request waterfalls by starting a query in a parent component and reading the result in a child component. This pattern enables the parent to start fetching data before the child component renders.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useBackgroundQuery, useReadQuery } from \"@apollo/client/react\";\n\nfunction Parent() {\n  // Start fetching immediately\n  const [queryRef] = useBackgroundQuery(GET_DOG, {\n    variables: { id: \"3\" },\n  });\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Child queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction Child({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  // Read the query result\n  const { data } = useReadQuery(queryRef);\n\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### When to Use\n\n- **Avoiding waterfalls**: Start fetching data in a parent (preferably above a suspense boundary) before child components render\n- **Preloading data**: Begin fetching before the component that needs the data is ready\n- **Parallel queries**: Start multiple queries at once in a parent component\n\n### Return Values\n\n`useBackgroundQuery` returns a tuple:\n\n```typescript\nconst [\n  queryRef, // QueryRef to pass to useReadQuery\n  { refetch, fetchMore, subscribeToMore }, // Helper functions\n] = useBackgroundQuery(QUERY, options);\n```\n\n`useReadQuery` returns the query result:\n\n```typescript\nconst {\n  data, // Query result data (always defined)\n  dataState, // \"complete\" | \"streaming\" | \"partial\" | \"empty\"\n  error, // ApolloError (if errorPolicy allows)\n  networkStatus, // Detailed network state (1-8)\n} = useReadQuery(queryRef);\n```\n\n## useLoadableQuery\n\nUse `useLoadableQuery` to imperatively load a query in response to a user interaction (like a button click) instead of on component mount.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useLoadableQuery, useReadQuery } from \"@apollo/client/react\";\nimport { GET_GREETING } from \"./queries.generated\";\n\nfunction App() {\n  const [loadGreeting, queryRef] = useLoadableQuery(GET_GREETING);\n\n  return (\n    <>\n      <button\n        onClick={() => loadGreeting({ variables: { language: \"english\" } })}\n      >\n        Load Greeting\n      </button>\n      <Suspense fallback={<div>Loading...</div>}>\n        {queryRef && <Greeting queryRef={queryRef} />}\n      </Suspense>\n    </>\n  );\n}\n\nfunction Greeting({ queryRef }: { queryRef: QueryRef<GreetingData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.greeting.message}</div>;\n}\n```\n\n### Return Values\n\n```typescript\nconst [\n  loadQuery, // Function to load the query\n  queryRef, // QueryRef (null until loadQuery is called)\n  { refetch, fetchMore, subscribeToMore, reset }, // Helper functions\n] = useLoadableQuery(QUERY, options);\n```\n\n### When to Use\n\n- **User-triggered fetching**: Load data in response to user actions\n- **Lazy loading**: Defer data fetching until it's actually needed\n- **Progressive disclosure**: Load data for UI elements that may not be initially visible\n\n## createQueryPreloader\n\nThe `createQueryPreloader` function creates a `preloadQuery` function that can be used to initiate queries outside of React components. This is useful for preloading data before a component renders, such as in route loaders or event handlers.\n\n### Basic Usage\n\n```tsx\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createQueryPreloader } from \"@apollo/client/react\";\n\nconst client = new ApolloClient({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  cache: new InMemoryCache(),\n});\n\n// Create a preload function\nexport const preloadQuery = createQueryPreloader(client);\n```\n\n### Using preloadQuery with Route Loaders\n\n> **Note**: This example applies to React Router in non-framework mode. For React Router framework mode, see [integration-react-router.md](./integration-react-router.md).\n\nUse the preload function with React Router's `loader` function to begin loading data during route transitions:\n\n```tsx\nimport { preloadQuery } from \"@/lib/apollo-client\";\nimport { GET_DOG } from \"./queries.generated\";\n\n// React Router loader function\nexport async function loader({ params }: { params: { id: string } }) {\n  return preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n}\n\n// Route component\nexport default function DogRoute() {\n  const queryRef = useLoaderData();\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <DogDetails queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction DogDetails({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.dog.name}</h1>\n      <p>Breed: {data.dog.breed}</p>\n    </div>\n  );\n}\n```\n\n### Preventing Route Transitions Until Query Loads\n\nUse `preloadQuery.toPromise(queryRef)` to prevent route transitions until the query finishes loading:\n\n```tsx\nexport async function loader({ params }: { params: { id: string } }) {\n  const queryRef = preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n\n  // Wait for the query to complete before transitioning\n  return preloadQuery.toPromise(queryRef);\n}\n```\n\nWhen `preloadQuery.toPromise()` is used, the route transition waits for the query to complete, and the data renders immediately without showing a loading fallback.\n\n> **Note**: `preloadQuery.toPromise()` resolves with the `queryRef` itself (not the data) to encourage using `useReadQuery` for cache updates. If you need raw query data in your loader, use `client.query()` directly.\n\n### With Next.js Server Components\n\n> **Note**: For Next.js App Router, use the `PreloadQuery` component from `@apollo/client-integration-nextjs` instead. See [integration-nextjs.md](./integration-nextjs.md) for details.\n\n## useQueryRefHandlers\n\nThe `useQueryRefHandlers` hook provides access to `refetch` and `fetchMore` functions for queries initiated with `preloadQuery`, `useBackgroundQuery`, or `useLoadableQuery`. This is useful when you need to refetch or paginate data in components where the `queryRef` is passed through.\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n### Basic Usage\n\n```tsx\nimport { useQueryRefHandlers } from \"@apollo/client/react\";\n\nfunction Breeds({ queryRef }: { queryRef: QueryRef<BreedsData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            refetch();\n          });\n        }}\n      >\n        {isPending ? \"Refetching...\" : \"Refetch breeds\"}\n      </button>\n      <ul>\n        {data.breeds.map((breed) => (\n          <li key={breed.id}>{breed.name}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### With Pagination\n\nUse `fetchMore` to implement pagination:\n\n```tsx\nfunction Posts({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { fetchMore } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            fetchMore({\n              variables: {\n                offset: data.posts.length,\n              },\n            });\n          });\n        }}\n      >\n        {isPending ? \"Loading...\" : \"Load more\"}\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Preloaded queries**: Access refetch/fetchMore for queries initiated with `preloadQuery`\n- **Background queries**: Use in child components receiving `queryRef` from `useBackgroundQuery`\n- **Loadable queries**: Refetch or paginate queries initiated with `useLoadableQuery`\n- **React transitions**: Integrate with transitions to avoid showing loading fallbacks during refetches\n\n## Distinguishing Queries with queryKey\n\nApollo Client uses the combination of `query` and `variables` to uniquely identify each query. When multiple components use the same `query` and `variables`, they share the same identity and suspend at the same time, regardless of which component initiates the request.\n\nUse the `queryKey` option to ensure each hook has a unique identity:\n\n```tsx\nfunction UserProfile() {\n  // First query with unique key\n  const { data: userData } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-profile\"],\n  });\n\n  // Second query with same query and variables but different key\n  const { data: userPreview } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-preview\"],\n  });\n\n  return (\n    <div>\n      <UserCard user={userData.user} />\n      <UserSidebar user={userPreview.user} />\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Multiple instances**: When rendering multiple components that use the same query and variables\n- **Preventing shared suspension**: When you want independent control over when each query suspends\n- **Separate cache entries**: When you need to maintain separate cache states for the same query\n\n> **Note**: Each item in the `queryKey` array must be a stable identifier to prevent infinite fetches.\n\n## Suspense Boundaries and Error Handling\n\n### Suspense Boundaries\n\nWrap components that use Suspense hooks with `<Suspense>` boundaries to handle loading states. Place boundaries strategically to control the granularity of loading indicators.\n\n```tsx\nfunction App() {\n  return (\n    <>\n      {/* Top-level loading for entire page */}\n      <Suspense fallback={<PageSpinner />}>\n        <Header />\n        <Content />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Content() {\n  return (\n    <>\n      <MainSection />\n      {/* Granular loading for sidebar */}\n      <Suspense fallback={<SidebarSkeleton />}>\n        <Sidebar />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Error Boundaries\n\nSuspense hooks throw errors to React error boundaries instead of returning them. Use error boundaries to handle GraphQL errors.\n\n```tsx\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nfunction App() {\n  return (\n    <ErrorBoundary\n      fallback={({ error }) => (\n        <div>\n          <h2>Something went wrong</h2>\n          <p>{error.message}</p>\n        </div>\n      )}\n    >\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id=\"3\" />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n```\n\n### Custom Error Policies\n\nUse `errorPolicy` to control how errors are handled:\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data, error } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    errorPolicy: \"all\", // Return both data and errors\n  });\n\n  return (\n    <>\n      <div>Name: {data?.dog?.name ?? \"Unknown\"}</div>\n      {error && <div>Warning: {error.message}</div>}\n    </>\n  );\n}\n```\n\n## Transitions\n\nUse React transitions to avoid showing loading UI when updating state. Transitions keep the previous UI visible while new data is fetching.\n\n### Using startTransition\n\n```tsx\nimport { useState, Suspense, startTransition } from \"react\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => {\n          // Wrap state update in startTransition\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Using useTransition\n\nUse `useTransition` to get an `isPending` flag for visual feedback during transitions.\n\n```tsx\nimport { useState, Suspense, useTransition } from \"react\";\n\nfunction DogSelector() {\n  const [isPending, startTransition] = useTransition();\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        style={{ opacity: isPending ? 0.5 : 1 }}\n        value={selectedDog}\n        onChange={(e) => {\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n## Avoiding Request Waterfalls\n\nRequest waterfalls occur when a child component waits for the parent to finish rendering before it can start fetching its own data. Use `useBackgroundQuery` to start fetching child data earlier in the component tree.\n\n> **Note**: When one query depends on the result of another query (e.g., the child query needs an ID from the parent query), the waterfall is unavoidable. The best solution is to restructure your schema to fetch all needed data in a single nested query.\n\n### Example: Independent Queries\n\nWhen queries don't depend on each other, use `useBackgroundQuery` to start them in parallel:\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: String!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nconst GET_POSTS = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n    }\n  }\n`;\n\nfunction Parent() {\n  // Both queries start immediately - no waterfall\n  const [userRef] = useBackgroundQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const [postsRef] = useBackgroundQuery(GET_POSTS);\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserProfile queryRef={userRef} />\n      <PostsList queryRef={postsRef} />\n    </Suspense>\n  );\n}\n\nfunction UserProfile({ queryRef }: { queryRef: QueryRef<UserData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>User: {data.user.name}</div>;\n}\n\nfunction PostsList({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <ul>\n      {data.posts.map((post) => (\n        <li key={post.id}>{post.title}</li>\n      ))}\n    </ul>\n  );\n}\n```\n\n## Fetch Policies\n\nSuspense hooks support most of the same fetch policies as `useQuery`, controlling how the query interacts with the cache. Note that `cache-only` and `standby` are not supported by Suspense hooks.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n\n### Usage Examples\n\n```tsx\n// Always fetch fresh data\nconst { data } = useSuspenseQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Prefer cached data\nconst { data } = useSuspenseQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data } = useSuspenseQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n```\n\n## Streaming SSR or React Server Components\n\nApollo Client integrates with modern React frameworks that support Streaming SSR and React Server Components. For detailed setup instructions specific to your framework, see:\n\n- **Next.js App Router**: [integration-nextjs.md](./integration-nextjs.md) - Includes React Server Components, PreloadQuery component, and streaming SSR\n- **React Router**: [integration-react-router.md](./integration-react-router.md) - Framework mode with SSR support\n- **TanStack Start**: [integration-tanstack-start.md](./integration-tanstack-start.md) - Full-stack React framework with SSR\n\nThese guides cover:\n\n- Framework-specific client setup and configuration\n- Preloading queries for optimal performance\n- Streaming SSR with `useBackgroundQuery` and Suspense\n- Error handling in server-rendered environments\n\n## Conditional Queries\n\n### Using skipToken\n\nUse `skipToken` to conditionally skip queries without TypeScript issues. When `skipToken` is used, the component won't suspend and `data` will be `undefined`.\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data, dataState } = useSuspenseQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  if (dataState !== \"complete\") {\n    return <p>Select a user</p>;\n  }\n\n  return <Profile user={data.user} />;\n}\n```\n\n### Conditional Rendering\n\nAlternatively, use conditional rendering to control when Suspense hooks are called. This provides better type safety and clearer component logic.\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  if (!userId) {\n    return <p>Select a user</p>;\n  }\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserDetails userId={userId} />\n    </Suspense>\n  );\n}\n\nfunction UserDetails({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <Profile user={data.user} />;\n}\n```\n\n> **Note**: Using conditional rendering with `skipToken` provides better type safety and avoids issues with required variables. The `skip` option is deprecated in favor of `skipToken`.\n","docs/agent-skills/apollo-client/references/troubleshooting.md":"# Troubleshooting Reference\n\n## Table of Contents\n\n- [Setup Issues](#setup-issues)\n- [Cache Issues](#cache-issues)\n- [TypeScript Issues](#typescript-issues)\n- [Performance Issues](#performance-issues)\n- [DevTools Usage](#devtools-usage)\n- [Common Error Messages](#common-error-messages)\n\n## Setup Issues\n\n### Provider Not Found\n\n**Error:** `Could not find \"client\" in the context or passed in as an option`\n\n**Cause:** Component is not wrapped with `ApolloProvider`.\n\n**Solution:**\n\n```tsx\n// Ensure ApolloProvider wraps your app\nimport { ApolloProvider } from \"@apollo/client\";\n\nfunction App() {\n  return (\n    <ApolloProvider client={client}>\n      <YourApp />\n    </ApolloProvider>\n  );\n}\n```\n\n### Multiple Apollo Clients\n\n**Problem:** Unintended cache isolation or conflicting states.\n\n**Solution:** Use a single client instance or explicitly manage multiple clients:\n\n```tsx\n// Single client (recommended)\nconst client = new ApolloClient({\n  /* ... */\n});\n\nexport function App() {\n  return (\n    <ApolloProvider client={client}>\n      <Router />\n    </ApolloProvider>\n  );\n}\n\n// Multiple clients (rare use case)\nconst publicClient = new ApolloClient({\n  uri: \"/public/graphql\",\n  cache: new InMemoryCache(),\n});\nconst adminClient = new ApolloClient({\n  uri: \"/admin/graphql\",\n  cache: new InMemoryCache(),\n});\n\nfunction AdminSection() {\n  return (\n    <ApolloProvider client={adminClient}>\n      <AdminDashboard />\n    </ApolloProvider>\n  );\n}\n```\n\n### Client Created in Component\n\n**Problem:** New client on every render causes cache loss.\n\n**Solution:** Create client outside component or use a ref pattern:\n\n```tsx\n// Bad - new client on every render\nfunction App() {\n  const client = new ApolloClient({\n    /* ... */\n  }); // Don't do this!\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Module-level client definition\n// Okay if there is a 100% guarantee this application will never use SSR\nconst client = new ApolloClient({\n  /* ... */\n});\nfunction App() {\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Good - store Apollo Client in a ref that is initialized once\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    storeRef.current = makeApolloClient();\n  }\n  return storeRef.current;\n}\n\n// Better - singleton global in non-SSR environments to survive unmounts\nconst singleton = Symbol.for(\"ApolloClientSingleton\");\ndeclare global {\n  interface Window {\n    [singleton]?: ApolloClient;\n  }\n}\n\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    if (typeof window === \"undefined\") {\n      storeRef.current = makeApolloClient();\n    } else {\n      window[singleton] ??= makeApolloClient();\n      storeRef.current = window[singleton];\n    }\n  }\n  return storeRef.current;\n}\n// Note: this second option might need manual removal between tests\n```\n\n## Cache Issues\n\n### Stale Data Not Updating\n\n**Problem:** UI doesn't reflect mutations or other updates.\n\n**Solution 1:** Verify cache key identification:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Ensure proper identification\n    Product: {\n      keyFields: [\"id\"], // or ['sku'] if no id field\n    },\n  },\n});\n```\n\n**Solution 2:** Update cache after mutations:\n\n```tsx\nconst [deleteProduct] = useMutation(DELETE_PRODUCT, {\n  update: (cache, { data }) => {\n    cache.evict({ id: cache.identify(data.deleteProduct) });\n    cache.gc();\n  },\n});\n```\n\n**Solution 3:** Use appropriate fetch policy:\n\n```tsx\nconst { data } = useQuery(GET_PRODUCTS, {\n  fetchPolicy: \"cache-and-network\", // Always fetch fresh data\n});\n```\n\n### Missing Cache Updates After Mutation\n\n**Problem:** New items don't appear in lists after creation.\n\n**Solution:** Manually update the cache:\n\n```tsx\nconst [createProduct] = useMutation(CREATE_PRODUCT, {\n  update: (cache, { data }) => {\n    const existing = cache.readQuery<{ products: Product[] }>({\n      query: GET_PRODUCTS,\n    });\n\n    cache.writeQuery({\n      query: GET_PRODUCTS,\n      data: {\n        products: [...(existing?.products ?? []), data.createProduct],\n      },\n    });\n  },\n});\n```\n\n### Pagination Cache Issues\n\n**Problem:** Paginated data not merging correctly.\n\n**Solution:** Configure proper type policies:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        products: {\n          keyArgs: [\"category\"], // Only category creates new cache entries\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Cache Normalization Problems\n\n**Problem:** Objects with same ID showing different data in different queries.\n\n**Debug:** Check cache contents:\n\n```typescript\n// In DevTools console or component\nconsole.log(client.cache.extract());\n```\n\n**Solution:** Ensure consistent `__typename` and `id` fields:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Always include id\n    name\n  }\n}\n```\n\n## TypeScript Issues\n\n### Type Generation Setup\n\n**Problem:** No type safety for GraphQL operations.\n\n**Solution:** Set up GraphQL Code Generator with the [recommended starter configuration](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration), as described in the [Skill](../SKILL.md).\n\n### Using Generated Types\n\n```tsx\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUsersDocument, GetUsersQuery } from \"./generated/graphql\";\n\nfunction UserList() {\n  // Fully typed without manual type annotations\n  const { data, loading, error } = useQuery(GetUsersDocument);\n\n  // data.users is automatically typed as GetUsersQuery['users']\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Performance Issues\n\n### Over-Fetching\n\n**Problem:** Fetching more data than needed.\n\n**Solution:** Select only required fields:\n\n```graphql\n# Bad - fetching everything\nquery GetUsers {\n  users {\n    id\n    name\n    email\n    profile { ... }\n    posts { ... }\n    friends { ... }\n  }\n}\n\n# Good - fetch what's needed\nquery GetUserNames {\n  users {\n    id\n    name\n  }\n}\n```\n\n### N+1 Queries\n\n**Problem:** Multiple network requests for related data.\n\n**Solution:** Structure queries to batch requests. Best practice: use query colocation and compose queries from fragments defined on child components.\n\n```graphql\n# Bad - separate queries\nquery GetUser($id: ID!) {\n  user(id: $id) {\n    id\n    name\n  }\n}\nquery GetUserPosts($userId: ID!) {\n  posts(userId: $userId) {\n    id\n    title\n  }\n}\n\n# Good - single query\nquery GetUserWithPosts($id: ID!) {\n  user(id: $id) {\n    id\n    name\n    posts {\n      id\n      title\n    }\n  }\n}\n```\n\n### Unnecessary Re-Renders\n\n**Problem:** Components re-render when unrelated cache data changes.\n\n**Solution:** Use `useFragment` and data masking for selective field reading. If that is not possible, `useQuery` with `@nonreactive` directives might be an alternative.\n\n```tsx\n// Prefer useFragment with data masking\nconst { data } = useFragment({\n  fragment: USER_FRAGMENT,\n  from: { __typename: \"User\", id },\n});\n\n// Alternative: use @nonreactive directive\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      # This field won't trigger re-renders when it changes\n      metadata @nonreactive {\n        lastSeen\n        preferences\n      }\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_USER, {\n  variables: { id },\n});\n```\n\n### Cache Misses\n\n**Debug:** Use Apollo DevTools to inspect cache.\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  // DevTools are enabled by default in development\n  // Only configure this when you need to enable them in production\n  devtools: {\n    enabled: true,\n  },\n});\n```\n\n## DevTools Usage\n\n### Installing Apollo DevTools\n\nInstall the browser extension:\n\n- [Chrome](https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)\n- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/)\n\n### Enabling DevTools\n\nDevTools are enabled by default in development. Only configure this setting if you need to enable them in production:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  devtools: {\n    enabled: true, // Set to true to enable in production\n  },\n});\n```\n\n### DevTools Features\n\n1. **Cache Inspector**: View normalized cache contents\n2. **Queries**: See active queries and their states\n3. **Mutations**: Track mutation history\n4. **Explorer**: Build and test queries against your schema\n5. **Memoization Limits**: Monitor and track cache memoization\n6. **Cache Writes**: Track all writes to the cache\n\n### Debugging Cache\n\n```typescript\n// Log cache contents\nconsole.log(JSON.stringify(client.cache.extract(), null, 2));\n\n// Check specific object using cache.identify\nconsole.log(\n  client.cache.readFragment({\n    id: cache.identify({ __typename: \"User\", id: 1 }),\n    fragment: gql`\n      fragment _ on User {\n        id\n        name\n        email\n      }\n    `,\n  })\n);\n```\n\n## Common Error Messages\n\n### \"Missing field 'X' in {...}\"\n\n**Cause:** Query doesn't include required field for cache normalization.\n\n**Solution:** Include `id` and `__typename`:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Required for caching\n    __typename # Usually added automatically\n    name\n  }\n}\n```\n\n**Additional advice**: Read the full error message thoroughly.\n\n### \"Store reset while query was in flight\"\n\n**Cause:** `client.resetStore()` called during active queries.\n\n**Solution:** Wait for queries to complete or use `clearStore()`:\n\n```typescript\n// Option 1: Clear without refetching\nawait client.clearStore();\n\n// Option 2: Reset and refetch active queries\nawait client.resetStore();\n```\n\n### \"Invariant Violation: X\"\n\n**Cause:** Various configuration or usage errors.\n\n**Common fixes:**\n\n- Ensure `ApolloProvider` wraps the component tree\n- Check that `gql` tagged templates are valid GraphQL\n- Verify cache configuration matches your schema\n\n### \"Cannot read property 'X' of undefined\"\n\n**Cause:** Accessing data before query completes.\n\n**Solution:** Check `dataState` for proper type narrowing:\n\n```tsx\nconst { data, dataState } = useQuery(GET_USER);\n\n// dataState can be \"complete\", \"partial\", \"streaming\", or \"empty\"\n// It describes the completeness of the data, not a loading state\nif (dataState === \"empty\") return <Spinner />;\n\n// Now data is guaranteed to exist\nreturn <div>{data.user.name}</div>;\n\n// Or use optional chaining\nreturn <div>{data?.user?.name}</div>;\n```\n","docs/agent-skills/apollo-client/references/typescript-codegen.md":"# TypeScript Code Generation\n\nThis guide covers setting up GraphQL Code Generator for type-safe Apollo Client usage with TypeScript.\n\n## Installation\n\n```bash\nnpm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node\n```\n\n## Configuration\n\nCreate a `codegen.ts` file in your project root:\n\n```typescript\n// codegen.ts\nimport { CodegenConfig } from \"@graphql-codegen/cli\";\n\nconst config: CodegenConfig = {\n  overwrite: true,\n  schema: \"<URL_OF_YOUR_GRAPHQL_API>\",\n  // This assumes that all your source files are in a top-level `src/` directory - you might need to adjust this to your file structure\n  documents: [\"src/**/*.{ts,tsx}\"],\n  // Don't exit with non-zero status when there are no documents\n  ignoreNoDocuments: true,\n  generates: {\n    // Use a path that works the best for the structure of your application\n    \"./src/types/__generated__/graphql.ts\": {\n      plugins: [\"typescript\", \"typescript-operations\", \"typed-document-node\"],\n      config: {\n        avoidOptionals: {\n          // Use `null` for nullable fields instead of optionals\n          field: true,\n          // Allow nullable input fields to remain unspecified\n          inputValue: false,\n        },\n        // Use `unknown` instead of `any` for unconfigured scalars\n        defaultScalarType: \"unknown\",\n        // Apollo Client always includes `__typename` fields\n        nonOptionalTypename: true,\n        // Apollo Client doesn't add the `__typename` field to root types so\n        // don't generate a type for the `__typename` for root operation types.\n        skipTypeNameForRoot: true,\n      },\n    },\n  },\n};\n\nexport default config;\n```\n\n## Enable Data Masking\n\nTo enable data masking with GraphQL Code Generator, create a type declaration file to inform Apollo Client about the generated types:\n\n```typescript\n// apollo-client.d.ts\nimport { GraphQLCodegenDataMasking } from \"@apollo/client/masking\";\n\ndeclare module \"@apollo/client\" {\n  export interface TypeOverrides\n    extends GraphQLCodegenDataMasking.TypeOverrides {}\n}\n```\n\n## Running Code Generation\n\nAdd a script to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  }\n}\n```\n\nRun code generation:\n\n```bash\nnpm run codegen\n```\n\n## Usage with Apollo Client\n\nThe typed-document-node plugin generates `TypedDocumentNode` types that Apollo Client hooks automatically infer.\n\n### Defining Operations\n\nDefine your operations inline with the `if (false)` pattern. This allows GraphQL Code Generator to detect and extract operations without executing the code at runtime (bundlers omit this dead code during minification):\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\n// This query will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n```\n\n### Using Generated Types\n\nAfter running `npm run codegen`, import the generated `TypedDocumentNode`:\n\n```typescript\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  // ... other logic ...\n\n  return <div>{data?.user.name}</div>;\n}\n```\n\n## Important Notes\n\n- The typed-document-node plugin might have a bundle size tradeoff but can prevent inconsistencies and is best suited for usage with LLMs, so it is recommended for most applications.\n- See the [GraphQL Code Generator documentation](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration) for other recommended configuration patterns if required.\n- Apollo Client hooks automatically infer types from `TypedDocumentNode` - never use manual generics like `useQuery<QueryType, VariablesType>()`.\n",".cursorrules (react-graphql-apollo-client-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for GraphQL development with Apollo Client integration.\"\nglobs: **/*\nalwaysApply: false\n---\n// React + GraphQL (Apollo Client) .cursorrules\n\n// Prefer functional components with hooks\n\nconst preferFunctionalComponents = true;\n\n// GraphQL and Apollo Client best practices\n\nconst graphqlBestPractices = [\n  \"Use Apollo Client for state management and data fetching\",\n  \"Implement query components for data fetching\",\n  \"Utilize mutations for data modifications\",\n  \"Use fragments for reusable query parts\",\n  \"Implement proper error handling and loading states\",\n];\n\n// Folder structure\n\nconst folderStructure = `\nsrc/\n  components/\n  graphql/\n    queries/\n    mutations/\n    fragments/\n  hooks/\n  pages/\n  utils/\n`;\n\n// Additional instructions\n\nconst additionalInstructions = `\n1. Use Apollo Provider at the root of your app\n2. Implement custom hooks for Apollo operations\n3. Use TypeScript for type safety with GraphQL operations\n4. Utilize Apollo Client's caching capabilities\n5. Implement proper error boundaries for GraphQL errors\n6. Use Apollo Client DevTools for debugging\n7. Follow naming conventions for queries, mutations, and fragments\n`;\n"},"files":{".claude/commands/chores.md":"---\nallowed-tools: Bash(git commit:*)\ndescription: Chores\n---\n\n## Context\n\n- Current git status: !`git status`\n- Current git diff (staged and unstaged changes): !`git diff HEAD`\n- Current branch: !`git branch --show-current`\n- Recent commits: !`git log --oneline -10`\n\n## Your task\n\nFetch the latest changes from the remote repository. If the current branch is not up to date, don't do anything, but inform the user about it.\n\nTake a note if the current git status is clean.\n\nRun the npm scripts `format`, `build`, `extract-api:only`, `update-size-limits` after each other in that order.\n\nIf before running the scripts, the git status was clean, create a new commit with the message \"chores\".\n","CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Apollo Client Overview\n\nApollo Client is a comprehensive caching GraphQL client for TypeScript/JavaScript. The project is maintained at version 4.x.x and uses a monorepo structure with npm workspaces.\n\n## Common Development Commands\n\n### Build Commands\n\n- `npm run build` - Build the entire project\n- `npm run build -w codegen` - Build specific workspace (e.g., codegen)\n\n### Testing Commands\n\n- `npm test` - Run all tests with Jest\n- `npm run test:type-benches` - Run type benchmarks\n- `npm run test:memory` - Run memory tests\n\n### Code Quality Commands\n\n- `npm run typecheck` - Run TypeScript type checking\n- `npm run lint` - Run ESLint with extended rules\n- `npm run format` - Format code with Prettier\n- `npm run check:format` - Check if code is formatted correctly\n- `npm run knip` - Check for unused files/exports\n- `npm run madge` - Check for circular dependencies\n\n### Documentation & API\n\n- `npm run extract-api` - Generate API documentation\n- `npm run typedoc` - Generate TypeDoc documentation\n- `npm run docmodel` - Generate API documentation model\n\n### Release & Publishing\n\n- `npx changeset` - Create a changeset for your changes\n\n## High-Level Architecture\n\n### Core Modules\n\n1. **Core (`src/core/`)** - Main Apollo Client implementation\n\n   - `ApolloClient.ts` - Main client class\n   - `ObservableQuery.ts` - Observable query implementation\n   - `QueryManager.ts` - Query lifecycle management\n\n2. **Cache (`src/cache/`)** - Caching layer\n\n   - `inmemory/` - InMemoryCache implementation (default cache)\n   - `core/` - Base cache abstractions\n\n3. **Link (`src/link/`)** - Network layer abstraction\n\n   - `http/` - HTTP link for GraphQL over HTTP\n   - `batch/` - Batch link for combining queries\n   - `error/` - Error handling link\n   - `schema/` - Schema link for local execution\n   - `ws/` - WebSocket link for subscriptions\n\n4. **React Integration (`src/react/`)** - React hooks and components\n\n   - `hooks/` - React hooks (useQuery, useMutation, etc.)\n   - `ssr/` - Server-side rendering support\n   - `context/` - React context providers\n\n5. **Utilities (`src/utilities/`)** - Shared utilities\n\n   - `graphql/` - GraphQL document utilities\n   - `internal/` - Internal utilities\n   - `invariant/` - Error handling utilities\n\n6. **Testing (`src/testing/`)** - Testing utilities\n\n   - `core/` - Core testing utilities\n   - `react/` - React testing utilities (MockedProvider)\n\n7. **Masking (`src/masking/`)** - Data masking for fragment colocation\n\n8. **Local State (`src/local-state/`)** - Local state management\n\n### TypeScript Configuration\n\n- Uses TypeScript 5.7.3 with strict mode enabled\n- Module resolution: NodeNext\n- Target: ESNext\n- Separate tsconfig files for different purposes:\n  - `tsconfig.json` - Main configuration\n  - `tsconfig.build.json` - Build configuration\n  - `tsconfig.tests.json` - Test configuration\n\n### Testing Infrastructure\n\n- Jest with ts-jest for testing\n- Custom test environment extending jsdom\n- React Testing Library for React component tests\n- Multiple React version testing (17, 18, 19)\n- Coverage reporting with lcov\n\n### Build System\n\n- Uses custom build script (`config/build.ts`)\n- API Extractor for API documentation\n- Size limit checking for bundle size\n- Source map verification\n- Publint for package publishing validation\n\n### Code Organization Patterns\n\n1. **Exports** - All public exports go through index files\n2. **Internal APIs** - Use `@apollo/client/*/internal` paths\n3. **Environment-specific builds** - Production/development variants\n4. **Type safety** - Heavy use of TypeScript generics and type inference\n5. **Reactive programming** - Uses RxJS for observables\n6. **Immutability** - Emphasizes immutable data structures\n\n### Contributing Guidelines\n\n- All changes require a changeset (`npx changeset`)\n- PRs should include tests for bug fixes\n- Follow existing code style (enforced by ESLint/Prettier)\n- Large features need design discussion first\n- Code review required from core contributors\n\n### Important Notes\n\n- This is a monorepo with workspaces: main package, codegen, and codemods\n- The project is currently at version 4.x.x (release candidate)\n- Supports multiple peer dependencies (React 17/18/19, GraphQL 16)\n- Uses patch-package for dependency patches\n- Has extensive CI checks that must pass before merging\n\n## Additional Instructions\n\n@.claude/documentation.md\n@.claude/hooks.md\n","docs/agent-skills/apollo-client/SKILL.md":"---\nname: apollo-client\ndescription: >\n  Guide for building React applications with Apollo Client 4.x. Use this skill when:\n  (1) setting up Apollo Client in a React project,\n  (2) writing GraphQL queries or mutations with hooks,\n  (3) configuring caching or cache policies,\n  (4) managing local state with reactive variables,\n  (5) troubleshooting Apollo Client errors or performance issues.\nlicense: MIT\ncompatibility: React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.\nmetadata:\n  author: apollographql\n  version: \"1.0.0\"\nallowed-tools: Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep\n---\n\n# Apollo Client 4.x Guide\n\nApollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Version 4.x brings improved caching, better TypeScript support, and React 19 compatibility.\n\n## Integration Guides\n\nChoose the integration guide that matches your application setup:\n\n- **[Client-Side Apps](references/integration-client.md)** - For client-side React applications without SSR (Vite, Create React App, etc.)\n- **[Next.js App Router](references/integration-nextjs.md)** - For Next.js applications using the App Router with React Server Components\n- **[React Router Framework Mode](references/integration-react-router.md)** - For React Router 7 applications with streaming SSR\n- **[TanStack Start](references/integration-tanstack-start.md)** - For TanStack Start applications with modern routing\n\nEach guide includes installation steps, configuration, and framework-specific patterns optimized for that environment.\n\n## Quick Reference\n\n### Basic Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n### Basic Mutation\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst CREATE_USER = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (name: string) => {\n    await createUser({ variables: { input: { name } } });\n  };\n\n  return <button onClick={() => handleSubmit(\"John\")}>Create User</button>;\n}\n```\n\n### Suspense Query\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<p>Loading user...</p>}>\n      <UserProfile userId=\"1\" />\n    </Suspense>\n  );\n}\n```\n\n## Reference Files\n\nDetailed documentation for specific topics:\n\n- [TypeScript Code Generation](references/typescript-codegen.md) - GraphQL Code Generator setup for type-safe operations\n- [Queries](references/queries.md) - useQuery, useLazyQuery, polling, refetching\n- [Suspense Hooks](references/suspense-hooks.md) - useSuspenseQuery, useBackgroundQuery, useReadQuery, useLoadableQuery\n- [Mutations](references/mutations.md) - useMutation, optimistic UI, cache updates\n- [Fragments](references/fragments.md) - Fragment colocation, useFragment, useSuspenseFragment, data masking\n- [Caching](references/caching.md) - InMemoryCache, typePolicies, cache manipulation\n- [State Management](references/state-management.md) - Reactive variables, local state\n- [Error Handling](references/error-handling.md) - Error policies, error links, retries\n- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions\n\n## Key Rules\n\n### Query Best Practices\n\n- **Each page should generally only have one query, composed from colocated fragments.** Use `useFragment` or `useSuspenseFragment` in all non-page-components. Use `@defer` to allow slow fields below the fold to stream in later and avoid blocking the page load.\n- **Fragments are for colocation, not reuse.** Each fragment should describe exactly the data needs of a specific component, not be shared across components for common fields. See [Fragments reference](references/fragments.md) for details on fragment colocation and data masking.\n- Always handle `loading` and `error` states in UI when using non-suspenseful hooks (`useQuery`, `useLazyQuery`). When using Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n- Use `fetchPolicy` to control cache behavior per query\n- Use the TypeScript type server to look up documentation for functions and options (Apollo Client has extensive docblocks)\n\n### Mutation Best Practices\n\n- **If the schema permits, mutation return values should return everything necessary to update the cache.** Neither manual updates nor refetching should be necessary.\n- If the mutation response is insufficient, carefully weigh manual cache manipulation vs refetching. Manual updates risk missing server logic. Consider optimistic updates with a granular refetch if needed.\n- Handle errors gracefully in the UI\n- Use `refetchQueries` sparingly (prefer letting the cache update automatically)\n\n### Caching Best Practices\n\n- Configure `keyFields` for types without `id` field\n- Disable normalization by setting `keyFields: false` for types that don't include an identifier and are meant to group related fields under the parent\n- Use `typePolicies` for pagination and computed fields\n- Understand cache normalization to debug issues\n- **Enable [data masking](references/fragments.md#data-masking) for all new applications** - it prevents components from accessing fragment data they don't own, enforcing proper data boundaries and preventing over-rendering\n\n### Performance\n\n- Avoid over-fetching with proper field selection\n- Configure appropriate `fetchPolicy` per use case\n- Use `@defer` for incremental delivery of deferred query parts, and `@stream` for streaming list fields (`@stream` available in Apollo Client 4.1+)\n- **Prefer Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications** for better loading state handling and code simplicity\n\n## Ground Rules\n\n- ALWAYS use Apollo Client 4.x patterns (not v3 or earlier)\n- ALWAYS wrap your app with `ApolloProvider`\n- ALWAYS handle loading and error states when using non-suspenseful hooks\n- PREFER Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications for better DX\n- NEVER store Apollo Client in React state (use module-level or context)\n- PREFER `cache-first` for read-heavy data, `network-only` for real-time data\n- USE TypeScript for better type safety with GraphQL\n- IMPLEMENT proper cache updates instead of refetching entire queries\n- ADVISE the human steering you to look into Apollo DevTools when collaboratively debugging Apollo Client issues\n","docs/agent-skills/apollo-client/references/caching.md":"# Caching Reference\n\n## Table of Contents\n\n- [InMemoryCache Setup](#inmemorycache-setup)\n- [Cache Normalization](#cache-normalization)\n- [Type Policies](#type-policies)\n- [Field Policies](#field-policies)\n- [Pagination](#pagination)\n- [Cache Manipulation](#cache-manipulation)\n- [Garbage Collection](#garbage-collection)\n\n## InMemoryCache Setup\n\n### Basic Configuration\n\n```typescript\nimport { InMemoryCache } from \"@apollo/client\";\n\nconst cache = new InMemoryCache({\n  // Custom type policies\n  typePolicies: {\n    Query: {\n      fields: {\n        // Query-level field policies\n      },\n    },\n    User: {\n      keyFields: [\"id\"],\n      fields: {\n        // User-level field policies\n      },\n    },\n  },\n\n  // Custom type name handling (rare)\n  possibleTypes: {\n    Character: [\"Human\", \"Droid\"],\n    Node: [\"User\", \"Post\", \"Comment\"],\n  },\n});\n```\n\n### Constructor Options\n\n```typescript\nnew InMemoryCache({\n  // Define how types are identified in cache\n  typePolicies: {\n    /* ... */\n  },\n\n  // Interface/union type mappings between supertypes and their subtypes\n  possibleTypes: {\n    /* ... */\n  },\n\n  // Custom function to generate cache IDs (rare)\n  dataIdFromObject: (object) => {\n    if (object.__typename === \"Book\") {\n      return `Book:${object.isbn}`;\n    }\n    return defaultDataIdFromObject(object);\n  },\n});\n```\n\n## Cache Normalization\n\nApollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.\n\n### How Normalization Works\n\n```graphql\n# Query\nquery GetPost {\n  post(id: \"1\") {\n    id\n    title\n    author {\n      id\n      name\n    }\n  }\n}\n```\n\n```typescript\n// Normalized cache structure\n{\n  'Post:1': {\n    __typename: 'Post',\n    id: '1',\n    title: 'Hello World',\n    author: { __ref: 'User:42' }\n  },\n  'User:42': {\n    __typename: 'User',\n    id: '42',\n    name: 'John'\n  },\n  ROOT_QUERY: {\n    'post({\"id\":\"1\"})': { __ref: 'Post:1' }\n  }\n}\n```\n\n### Benefits of Normalization\n\n1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update\n2. **Deduplication**: Same objects aren't stored multiple times\n3. **Efficient updates**: Only changed objects trigger re-renders\n\n## Type Policies\n\n### keyFields\n\nCustomize how objects are identified in the cache.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Use ISBN instead of id for books\n    Book: {\n      keyFields: [\"isbn\"],\n    },\n\n    // Composite key\n    UserSession: {\n      keyFields: [\"userId\", \"deviceId\"],\n    },\n\n    // Nested key\n    Review: {\n      keyFields: [\"book\", [\"isbn\"], \"reviewer\", [\"id\"]],\n    },\n\n    // No key fields (singleton, only one object in cache per type)\n    AppSettings: {\n      keyFields: [],\n    },\n\n    // Disable normalization (objects of this type will be stored with their\n    // parent entity. The same object might end up multiple times in the cache\n    // and run out of sync. Use with caution, only if this object really relates\n    // to a property of their parent entity and cannot exist on its own.)\n    Address: {\n      keyFields: false,\n    },\n  },\n});\n```\n\n### merge Functions\n\nControl how incoming data merges with existing data.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Deep merge profile object\n        profile: {\n          merge: true, // Shorthand for deep merge\n        },\n\n        // Custom merge logic\n        notifications: {\n          merge(existing = [], incoming, { mergeObjects }) {\n            // Prepend new notifications\n            return [...incoming, ...existing];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## Field Policies\n\n### read Function\n\nTransform cached data when reading.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Computed field\n        fullName: {\n          read(_, { readField }) {\n            const firstName = readField(\"firstName\");\n            const lastName = readField(\"lastName\");\n            return `${firstName} ${lastName}`;\n          },\n        },\n\n        // Transform existing field\n        birthDate: {\n          read(existing) {\n            return existing ? new Date(existing) : null;\n          },\n        },\n\n        // Default value\n        role: {\n          read(existing = \"USER\") {\n            return existing;\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### merge Function\n\nControl how incoming data is stored.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Accumulate items instead of replacing\n        friends: {\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n\n        // Merge objects deeply\n        settings: {\n          merge(existing, incoming, { mergeObjects }) {\n            return mergeObjects(existing, incoming);\n          },\n        },\n      },\n    },\n\n    Query: {\n      fields: {\n        // Merge paginated results\n        posts: {\n          keyArgs: [\"category\"], // Only category affects cache key\n          merge(existing = { items: [] }, incoming) {\n            return {\n              ...incoming,\n              items: [...existing.items, ...incoming.items],\n            };\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### keyArgs\n\nControl which arguments affect cache storage.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        // Different cache entry per userId only\n        // (limit, offset don't create new entries)\n        userPosts: {\n          keyArgs: [\"userId\"],\n        },\n\n        // No arguments affect cache key\n        // (useful for pagination)\n        feed: {\n          keyArgs: false,\n        },\n\n        // Nested argument\n        search: {\n          keyArgs: [\"filter\", [\"category\", \"status\"]],\n        },\n      },\n    },\n  },\n});\n```\n\n## Pagination\n\n### Offset-Based Pagination\n\n```typescript\nimport { offsetLimitPagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: offsetLimitPagination(),\n\n        // With key arguments\n        userPosts: offsetLimitPagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Cursor-Based Pagination (Relay Style)\n\n```typescript\nimport { relayStylePagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: relayStylePagination(),\n\n        // With key arguments\n        userPosts: relayStylePagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Custom Pagination\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        feed: {\n          keyArgs: false,\n\n          merge(existing, incoming, { args }) {\n            const merged = existing ? existing.slice(0) : [];\n            const offset = args?.offset ?? 0;\n\n            for (let i = 0; i < incoming.length; i++) {\n              merged[offset + i] = incoming[i];\n            }\n\n            return merged;\n          },\n\n          read(existing, { args }) {\n            const offset = args?.offset ?? 0;\n            const limit = args?.limit ?? existing?.length ?? 0;\n            return existing?.slice(offset, offset + limit);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### fetchMore for Pagination\n\n```tsx\nfunction PostList() {\n  const { data, fetchMore, loading } = useQuery(GET_POSTS, {\n    variables: { offset: 0, limit: 10 },\n  });\n\n  const loadMore = () => {\n    fetchMore({\n      variables: {\n        offset: data.posts.length,\n      },\n      // With proper type policies, no updateQuery needed\n    });\n  };\n\n  return (\n    <div>\n      {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}\n      <button onClick={loadMore} disabled={loading}>\n        Load More\n      </button>\n    </div>\n  );\n}\n```\n\n## Cache Manipulation\n\n### cache.readQuery\n\n```typescript\n// Read data from cache\nconst data = cache.readQuery({\n  query: GET_TODOS,\n});\n\n// With variables\nconst userData = cache.readQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n});\n```\n\n### cache.writeQuery\n\n```typescript\n// Write data to cache\ncache.writeQuery({\n  query: GET_TODOS,\n  data: {\n    todos: [\n      { __typename: \"Todo\", id: \"1\", text: \"Buy milk\", completed: false },\n    ],\n  },\n});\n\n// With variables\ncache.writeQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n  data: {\n    user: { __typename: \"User\", id: \"1\", name: \"John\" },\n  },\n});\n```\n\n### cache.readFragment / cache.writeFragment\n\n```typescript\n// Read a specific object - use cache.identify for safety\nconst user = cache.readFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\nconst user = cache.readFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Update a specific object\ncache.writeFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\ncache.writeFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n```\n\n### cache.modify\n\n```typescript\n// Modify fields directly\ncache.modify({\n  id: cache.identify(user),\n  fields: {\n    // Set new value\n    name: () => \"New Name\",\n\n    // Transform existing value\n    postCount: (existing) => existing + 1,\n\n    // Delete field\n    temporaryField: (_, { DELETE }) => DELETE,\n\n    // Add to array\n    friends: (existing, { toReference }) => [\n      ...existing,\n      toReference({ __typename: \"User\", id: \"2\" }),\n    ],\n  },\n});\n```\n\n### cache.evict\n\n```typescript\n// Remove object from cache\ncache.evict({ id: \"User:1\" });\n\n// Remove specific field\ncache.evict({ id: \"User:1\", fieldName: \"friends\" });\n\n// Remove with broadcast (trigger re-renders)\ncache.evict({ id: \"User:1\", broadcast: true });\n```\n\n## Garbage Collection\n\n### Manual Garbage Collection\n\n```typescript\n// After evicting objects, clean up dangling references\ncache.evict({ id: \"User:1\" });\ncache.gc();\n```\n\n### Retaining Objects\n\n```typescript\n// Prevent objects from being garbage collected\nconst release = cache.retain(\"User:1\");\n\n// Later, allow GC\nrelease();\ncache.gc();\n```\n\n### Inspecting Cache\n\n```typescript\n// Get all cached data\nconst cacheContents = cache.extract();\n\n// Restore cache state\ncache.restore(previousCacheContents);\n\n// Get identified object cache key\nconst userId = cache.identify({ __typename: \"User\", id: \"1\" });\n// Returns: 'User:1'\n```\n","docs/agent-skills/apollo-client/references/error-handling.md":"# Error Handling Reference (Apollo Client 4.x)\n\nNote that Apollo Client 4.x handles errors differently than Apollo Client 3.x.\nThis reference documents the updated error handling mechanisms, error types, and best practices for managing errors in your Apollo Client applications.\nFor older Apollo Client 3.x error handling documentation, see [Apollo Client 3.x Error Handling](https://www.apollographql.com/docs/react/v3/data/error-handling).\n\n## Table of Contents\n\n- [Understanding Errors](#understanding-errors)\n- [Error Types](#error-types)\n- [Identifying Error Types](#identifying-error-types)\n- [GraphQL Error Policies](#graphql-error-policies)\n- [Error Links](#error-links)\n- [Retry Link](#retry-link)\n- [Error Boundaries](#error-boundaries)\n\n## Understanding Errors\n\nErrors in Apollo Client fall into two main categories: **GraphQL errors** and **network errors**. Each category has specific error classes that provide detailed information about what went wrong.\n\n### GraphQL Errors\n\nGraphQL errors are related to server-side execution of a GraphQL operation:\n\n- **Syntax errors** (e.g., malformed query)\n- **Validation errors** (e.g., query includes a non-existent schema field)\n- **Resolver errors** (e.g., error while populating a query field)\n\nIf a syntax or validation error occurs, the server doesn't execute the operation. If resolver errors occur, the server can still return partial data.\n\nExample server response with GraphQL error:\n\n```json\n{\n  \"errors\": [\n    {\n      \"message\": \"Cannot query field \\\"nonexistentField\\\" on type \\\"Query\\\".\",\n      \"locations\": [{ \"line\": 2, \"column\": 3 }],\n      \"extensions\": {\n        \"code\": \"GRAPHQL_VALIDATION_FAILED\"\n      }\n    }\n  ],\n  \"data\": null\n}\n```\n\nIn Apollo Client 4.x, GraphQL errors are represented by the [`CombinedGraphQLErrors`](https://apollographql.com/docs/react/api/errors/CombinedGraphQLErrors) error type.\n\n### Network Errors\n\nNetwork errors occur when attempting to communicate with your GraphQL server:\n\n- `4xx` or `5xx` HTTP response status codes\n- Network unavailability\n- JSON parsing failures\n- Custom errors from Apollo Link request handlers\n\nNetwork errors might be represented by special error types, but if an api such as the `fetch` API throws a native error (e.g., `TypeError`), Apollo Client will pass it through as-is.\nThrown values that don't fulfill the standard `ErrorLike` interface are wrapped in the [`UnconventionalError`](https://apollographql.com/docs/react/api/errors/UnconventionalError) class, which fulfills the `ErrorLike` interface. As such, you can expect any error returned by Apollo Client to fulfill the `ErrorLike` interface.\n\n```ts\nexport interface ErrorLike {\n  message: string;\n  name: string;\n  stack?: string;\n}\n```\n\n## Error Types\n\nApollo Client 4.x provides specific error classes for different error scenarios:\n\n### CombinedGraphQLErrors\n\nRepresents GraphQL errors returned by the server. Most common error type in applications.\n\n```tsx\nimport { CombinedGraphQLErrors } from \"@apollo/client/errors\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, error } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // no need to check for nullishness of error, CombinedGraphQLErrors.is handles that\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    return (\n      <div>\n        {error.graphQLErrors.map((err, i) => (\n          <p key={i}>GraphQL Error: {err.message}</p>\n        ))}\n      </div>\n    );\n  }\n\n  return data ? <Profile user={data.user} /> : null;\n}\n```\n\n### CombinedProtocolErrors\n\nRepresents fatal transport-level errors during multipart HTTP subscription execution.\n\n### ServerError\n\nOccurs when the server responds with a non-200 HTTP status code.\n\n```tsx\nimport { ServerError } from \"@apollo/client/errors\";\n\nif (ServerError.is(error)) {\n  console.error(\"Server error:\", error.statusCode, error.result);\n}\n```\n\n### ServerParseError\n\nOccurs when the server response cannot be parsed as valid JSON.\n\n```tsx\nimport { ServerParseError } from \"@apollo/client/errors\";\n\nif (ServerParseError.is(error)) {\n  console.error(\"Invalid JSON response:\", error.bodyText);\n}\n```\n\n### LocalStateError\n\nRepresents errors in local state configuration or execution.\n\n### UnconventionalError\n\nWraps non-standard errors (e.g., thrown symbols or objects) to ensure consistent error handling.\n\n## Identifying Error Types\n\nEvery Apollo Client error class provides a static `is` method that reliably determines whether an error is of that specific type. This is more robust than `instanceof` because it avoids false positives/negatives.\n\n```ts\nimport {\n  CombinedGraphQLErrors,\n  CombinedProtocolErrors,\n  LocalStateError,\n  ServerError,\n  ServerParseError,\n  UnconventionalError,\n  ErrorLike,\n} from \"@apollo/client/errors\";\n\n// Anything returned in the `error` field of Apollo Client hooks or methods is of type `ErrorLike` or `undefined`.\nfunction handleError(error?: ErrorLike) {\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    console.error(\"GraphQL errors:\", error.graphQLErrors);\n  } else if (CombinedProtocolErrors.is(error)) {\n    // Handle multipart subscription protocol errors\n  } else if (LocalStateError.is(error)) {\n    // Handle errors thrown by the LocalState class\n  } else if (ServerError.is(error)) {\n    // Handle server HTTP errors\n    console.error(\"Server error:\", error.statusCode);\n  } else if (ServerParseError.is(error)) {\n    // Handle JSON parse errors\n  } else if (UnconventionalError.is(error)) {\n    // Handle errors thrown by irregular types\n  } else if (error) {\n    // Handle other errors\n  }\n}\n```\n\n## GraphQL Error Policies\n\nIf a GraphQL operation produces errors, the server's response might still include partial data:\n\n```json\n{\n  \"data\": {\n    \"getInt\": 12,\n    \"getString\": null\n  },\n  \"errors\": [\n    {\n      \"message\": \"Failed to get string!\"\n    }\n  ]\n}\n```\n\nBy default, Apollo Client throws away partial data and populates the `error` field. You can use partial results by defining an **error policy**:\n\n| Policy   | Description                                                                                                                                                                  |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `none`   | (Default) If the response includes errors, they are returned in `error` and response `data` is set to `undefined` even if the server returns `data`.                         |\n| `ignore` | Errors are ignored (`error` is not populated), and any returned `data` is cached and rendered as if no errors occurred. `data` may be `undefined` if a network error occurs. |\n| `all`    | Both `data` and `error` are populated and any returned `data` is cached, enabling you to render both partial results and error information.                                  |\n\n### Setting an Error Policy\n\n```tsx\nconst MY_QUERY = gql`\n  query WillFail {\n    badField # This field's resolver produces an error\n    goodField # This field is populated successfully\n  }\n`;\n\nfunction ShowingSomeErrors() {\n  const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: \"all\" });\n\n  if (loading) return <span>loading...</span>;\n\n  return (\n    <div>\n      <h2>Good: {data?.goodField}</h2>\n      {error && <pre>Bad: {error.message}</pre>}\n    </div>\n  );\n}\n```\n\n### Avoid setting a Global Error Policy\n\nWhile it is possible to set a global error policy using `defaultOptions`, in practice this is discouraged as it can lead to unexpected behavior and type safety issues. The return types of the TypeScript hooks may change depending on the `errorPolicy` passed into the hook, and this can conceptually not take global `defaultOptions` error policies into account. As such, it is best to set the `errorPolicy` per operation as needed.\n\n## Error Links\n\nThe `ErrorLink` can be used to e.g. log error globally or perform specific side effects based on errors happening.\n\nAn `ErrorLink` can't be used to swallow errors fully, but it can be used to retry an operation after handling an error, in which case the error wouldn't propagate. Otherwise, the most common use for `ErrorLink` is logging.\n\n```ts\nimport { ErrorLink } from \"@apollo/client/link/error\";\n\nconst errorLink = new ErrorLink(({ error, operation, forward }) => {\n  if (someCondition(error)) {\n    // Retry the request, returning the new observable\n    return forward(operation);\n  }\n\n  // Log the error for any unhandled GraphQL errors or network errors.\n  console.log(`[Error]: ${error.message}`);\n\n  // If nothing is returned from the error handler callback, the error will be\n  // emitted from the link chain as normal.\n});\n```\n\n### Retry Link\n\nAlternatively, you can use the `RetryLink` from `@apollo/client/link/retry` to implement retry logic for failed operations.\n\n```typescript\nimport { RetryLink } from \"@apollo/client/link/retry\";\n\nconst retryLink = new RetryLink({\n  delay: {\n    initial: 300,\n    max: Infinity,\n    jitter: true,\n  },\n  attempts: {\n    max: 5,\n    retryIf: (error, operation) => {\n      // Retry on network errors\n      return !!error && operation.operationName !== \"SensitiveOperation\";\n    },\n  },\n});\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  link: from([retryLink, errorLink, httpLink]),\n});\n```\n\n### Custom Retry Logic\n\n```typescript\nconst retryLink = new RetryLink({\n  attempts: (count, operation, error) => {\n    // Don't retry mutations\n    if (\n      operation.query.definitions.some(\n        (def) =>\n          def.kind === \"OperationDefinition\" && def.operation === \"mutation\"\n      )\n    ) {\n      return false;\n    }\n\n    // Retry up to 3 times on network errors\n    return count < 3 && !!error;\n  },\n  delay: (count) => {\n    // Exponential backoff\n    return Math.min(1000 * Math.pow(2, count), 30000);\n  },\n});\n```\n\n## Error Boundaries\n\nWhen using suspenseful hooks, you should use React Error Boundaries for graceful error handling.\n\n### Non-suspense per-Component Error Handling\n\n```tsx\nimport {\n  CombinedGraphQLErrors,\n  ServerError,\n  ServerParseError,\n} from \"@apollo/client/errors\";\n\nfunction SafeUserList() {\n  const { data, error, loading, refetch } = useQuery(GET_USERS, {\n    errorPolicy: \"all\",\n    notifyOnNetworkStatusChange: true,\n  });\n\n  // Handle network errors\n  if (ServerError.is(error) || ServerParseError.is(error)) {\n    return (\n      <Alert severity=\"error\">\n        <AlertTitle>Connection Error</AlertTitle>\n        Failed to load users. Please check your internet connection.\n        <Button onClick={() => refetch()}>Retry</Button>\n      </Alert>\n    );\n  }\n\n  // Handle GraphQL errors but still show available data\n  return (\n    <div>\n      {CombinedGraphQLErrors.is(error) && (\n        <Alert severity=\"warning\">\n          Some data may be incomplete: {error.graphQLErrors[0].message}\n        </Alert>\n      )}\n\n      {loading && <LinearProgress />}\n\n      {data?.users && <UserList users={data.users} />}\n    </div>\n  );\n}\n```\n","docs/agent-skills/apollo-client/references/fragments.md":"# Fragments Reference\n\nGraphQL fragments define a set of fields for a specific type. In Apollo Client, fragments are especially powerful when colocated with components to define each component's data requirements independently, creating a clear separation of concerns and enabling better component composition.\n\n## Table of Contents\n\n- [What Are Fragments](#what-are-fragments)\n- [Basic Fragment Syntax](#basic-fragment-syntax)\n- [Fragment Colocation](#fragment-colocation)\n- [Fragment Reading Hooks](#fragment-reading-hooks)\n- [Data Masking](#data-masking)\n- [Fragment Registry](#fragment-registry)\n- [TypeScript Integration](#typescript-integration)\n- [Best Practices](#best-practices)\n\n## What Are Fragments\n\nA GraphQL fragment defines a set of fields for a specific GraphQL type. Fragments are defined on a specific GraphQL type and can be included in operations using the spread operator (`...`).\n\nIn Apollo Client, fragments serve a specific purpose:\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment. This allows components to independently evolve their data requirements without creating artificial dependencies between unrelated parts of your application.\n\nFragments enable:\n\n1. **Component colocation**: Define the exact data requirements for a component alongside the component code\n2. **Independent evolution**: Change a component's data needs without affecting other components\n3. **Code organization**: Compose fragments together to build complete queries that mirror your component hierarchy\n\n## Basic Fragment Syntax\n\n### Defining a Fragment\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n    avatarUrl\n  }\n`;\n```\n\nEvery fragment includes:\n\n- A unique name (`UserFields`)\n- The type it operates on (`User`)\n- The fields to select\n\n### Using Fragments in Queries\n\nInclude fragments in queries using the spread operator:\n\n```typescript\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserFields\n    }\n  }\n\n  ${USER_FRAGMENT}\n`;\n```\n\nWhen using GraphQL Code Generator with the recommended configuration (typescript, typescript-operations, and typed-document-node plugins), fragments defined in your source files are automatically picked up and generated into typed document nodes. The generated fragment documents already include the fragment definition, so you don't need to interpolate them manually into queries.\n\n## Fragment Colocation\n\nFragment colocation is the practice of defining fragments in the same file as the component that uses them. This creates a clear contract between components and their data requirements.\n\n### Why Colocate Fragments\n\n- **Locality**: Data requirements live next to the code that uses them\n- **Maintainability**: Changes to component UI and data needs happen together\n- **Type safety**: TypeScript can infer exact types from colocated fragments\n- **Independence**: Components can evolve their data requirements without affecting other components\n\n### Colocation Pattern\n\nThe recommended pattern for colocating fragments with components:\n\n```tsx\nimport { gql, FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\n\n// Fragment definition\n// This will be picked up by Codegen to create `UserCard_UserFragmentDoc` in `./fragments.generated.ts`.\n// As that generated fragment document is correctly typed, we use that in the code going forward.\n// This fragment will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n}\n\n// This has been created from above fragment definition by CodeGen and is a correctly typed `TypedDocumentNode`\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated.ts\";\n\n// Component receives the (partially masked) parent object\nexport function UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a subscription to the fragment in the cache\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return (\n    <div>\n      <img src={data.avatarUrl} alt={data.name} />\n      <h2>{data.name}</h2>\n      <p>{data.email}</p>\n    </div>\n  );\n}\n```\n\n### Naming Convention\n\nA suggested naming pattern for fragments follows this convention:\n\n```text\n{ComponentName}_{propName}\n```\n\nWhere `propName` is the name of the prop the component receives containing the fragment data.\n\nExamples:\n\n- `UserCard_user` - Fragment for the `user` prop in the UserCard component\n- `PostList_posts` - Fragment for the `posts` prop in the PostList component\n- `CommentItem_comment` - Fragment for the `comment` prop in the CommentItem component\n\nThis convention makes it clear which component owns which fragment. However, you can choose a different naming convention based on your project's needs.\n\n**Note**: A component might accept fragment data through multiple props, in which case it would have multiple associated fragments. For example, a `CommentCard` component might accept both a `comment` prop and an `author` prop, resulting in `CommentCard_comment` and `CommentCard_author` fragments.\n\n### Composing Fragments\n\nParent components compose child fragments to build complete queries:\n\n```tsx\n// Child component\nimport { gql } from \"@apollo/client\";\n\nif (false) {\n  gql`\n    fragment UserAvatar_user on User {\n      id\n      avatarUrl\n      name\n    }\n  `;\n}\n\n// Parent component composes child fragments\nif (false) {\n  gql`\n    fragment UserProfile_user on User {\n      id\n      name\n      email\n      bio\n      ...UserAvatar_user\n    }\n  `;\n}\n\n// Page-level query composes all fragments\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserProfile_user\n      }\n    }\n  `;\n}\n```\n\nThis creates a hierarchy that mirrors your component tree.\n\n## Fragment Reading Hooks\n\nApollo Client provides hooks to read fragment data within components. These hooks work with data masking to ensure components only access the data they explicitly requested.\n\n### useSuspenseFragment\n\nFor components using Suspense and concurrent features:\n\n```tsx\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return <div>{data.name}</div>;\n}\n```\n\n### useFragment\n\nFor components not using Suspense:\n\n```tsx\nimport { useFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data, complete } = useFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  if (!complete) {\n    return <div>Loading...</div>;\n  }\n\n  return <div>{data.name}</div>;\n}\n```\n\nThe `complete` field indicates whether all fragment data is available in the cache.\n\n### Hook Options\n\nBoth hooks accept these options:\n\n```typescript\n{\n  // The fragment document (required)\n  fragment: TypedDocumentNode,\n\n  // The fragment name (optional in most cases)\n  // Only required if the fragment document contains multiple definitions\n  fragmentName?: string,\n\n  // The source data containing the fragment (required)\n  // Can be a single object or an array of objects\n  from: FragmentType<typeof fragment> | Array<FragmentType<typeof fragment>>,\n\n  // Variables for the fragment (optional)\n  variables?: Variables,\n}\n```\n\nWhen `from` is an array, the hook returns an array of results, allowing you to read fragments from multiple objects efficiently. **Note**: Array support for the `from` parameter was added in Apollo Client 4.1.0.\n\n## Data Masking\n\nData masking is a feature that prevents components from accessing data they didn't explicitly request through their fragments. This enforces proper data boundaries and prevents over-rendering.\n\n### Enabling Data Masking\n\nEnable data masking when creating your Apollo Client:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true, // Enable data masking\n});\n```\n\n### How Data Masking Works\n\nWith data masking enabled:\n\n1. Fragments return opaque `FragmentType` objects\n2. Components must use `useFragment` or `useSuspenseFragment` to unmask data\n3. Components can only access fields defined in their own fragments\n4. TypeScript enforces these boundaries at compile time\n\nWithout data masking:\n\n```tsx\n// ❌ Without data masking - component can access any data from parent\nfunction UserCard({ user }: { user: User }) {\n  // Can access any User field, even if not in fragment\n  return <div>{user.privateData}</div>;\n}\n```\n\nWith data masking:\n\n```tsx\n// ✅ With data masking - component can only access its fragment data\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // TypeScript error: 'privateData' doesn't exist on fragment type\n  // return <div>{data.privateData}</div>;\n\n  // Only fields from the fragment are accessible\n  return <div>{data.name}</div>;\n}\n```\n\n### Benefits of Data Masking\n\n- **Prevents over-rendering**: Components only re-render when their specific data changes\n- **Enforces boundaries**: Components can't accidentally depend on data they don't own\n- **Better refactoring**: Safe to modify parent queries without breaking child components\n- **Type safety**: TypeScript catches attempts to access unavailable fields\n\n## Fragment Registry\n\nThe fragment registry is an **alternative approach** to GraphQL Code Generator's automatic fragment inlining by name. It allows you to register fragments globally, making them available throughout your application by name reference.\n\n**Important**: GraphQL Code Generator automatically inlines fragments by name wherever they're used in your queries. Either approach is sufficient on its own—**you don't need to combine them**.\n\n### Creating a Fragment Registry\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createFragmentRegistry } from \"@apollo/client/cache\";\n\nexport const fragmentRegistry = createFragmentRegistry();\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache({\n    fragments: fragmentRegistry,\n  }),\n});\n```\n\n### Registering Fragments\n\nRegister fragments after defining them:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { fragmentRegistry } from \"./apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n  }\n`;\n\nfragmentRegistry.register(USER_FRAGMENT);\n```\n\nWith colocated fragments:\n\n```tsx\nimport { fragmentRegistry } from \"@/apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Register the fragment globally\nfragmentRegistry.register(UserCard_UserFragmentDoc);\n```\n\n### Using Registered Fragments\n\nOnce registered, fragments can be referenced by name in queries without explicit imports:\n\n```tsx\n// Fragment is available by name because it's registered\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserCard_user\n    }\n  }\n`;\n```\n\n### Approaches for Fragment Composition\n\nThere are three approaches to make child fragments available in parent queries:\n\n1. **GraphQL Code Generator inlining** (Recommended): CodeGen automatically inlines fragments by name. No manual work needed—just reference fragments by name in your queries.\n\n2. **Fragment Registry**: Manually register fragments to make them available by name. Useful for runtime scenarios where CodeGen isn't available.\n\n3. **Manual interpolation**: Explicitly import and interpolate child fragments into parent fragments:\n\n   ```typescript\n   import { CHILD_FRAGMENT } from \"./ChildComponent\";\n\n   const PARENT_FRAGMENT = gql`\n     fragment Parent_data on Data {\n       field\n       ...Child_data\n     }\n     ${CHILD_FRAGMENT}\n   `;\n   ```\n\n### Pros and Cons\n\n**GraphQL Code Generator inlining**:\n\n- ✅ Less work: Automatic, no manual registration needed\n- ❌ Larger bundle: Fragments are inlined into every query that uses them\n\n**Fragment Registry**:\n\n- ✅ Smaller bundle: Fragments are registered once, referenced by name\n- ❌ More work: Requires manual registration of each fragment\n- ❌ May cause issues with lazy-loaded modules if the module is not loaded before the query is executed\n- ✅ Best for deeply nested component trees where bundle size matters\n\n**Manual interpolation**:\n\n- ❌ Most work: Manual imports and interpolation required\n- ✅ Explicit: Clear fragment dependencies in code\n\n### Recommendation\n\nFor most applications using GraphQL Code Generator (as shown in this guide), **use the automatic inlining**—it requires no additional setup and works seamlessly. Consider the fragment registry only if bundle size becomes a concern in applications with deeply nested component trees.\n\n## TypeScript Integration\n\nApollo Client provides strong TypeScript support for fragments through GraphQL Code Generator.\n\n### Generated Types\n\nGraphQL Code Generator produces typed fragment documents:\n\n```typescript\n// Generated file: fragments.generated.ts\nexport type UserCard_UserFragment = {\n  __typename: \"User\";\n  id: string;\n  name: string;\n  email: string;\n  avatarUrl: string;\n} & { \" $fragmentName\"?: \"UserCard_UserFragment\" };\n\nexport const UserCard_UserFragmentDoc: TypedDocumentNode<\n  UserCard_UserFragment,\n  never\n>;\n```\n\n### Type-Safe Fragment Usage\n\nUse `FragmentType` to accept masked fragment data:\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // 'data' is fully typed as UserCard_UserFragment\n  return <div>{data.name}</div>;\n}\n```\n\n### Fragment Type Inference\n\nTypeScript infers types from fragment documents automatically:\n\n```tsx\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Types are inferred from the fragment\nconst { data } = useSuspenseFragment({\n  fragment: UserCard_UserFragmentDoc,\n  from: user,\n});\n\n// data.name is string\n// data.email is string\n// data.nonExistentField is a TypeScript error\n```\n\n### Parent-Child Type Safety\n\nWhen passing fragment data from parent to child:\n\n```tsx\n// Parent query\nconst { data } = useSuspenseQuery(GET_USER);\n\n// TypeScript ensures the query includes UserCard_user fragment\n// before allowing it to be passed to UserCard\n<UserCard user={data.user} />;\n```\n\n## Best Practices\n\n### Prefer Colocation Over Reuse\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment, even if multiple components currently need the same fields.\n\nSharing fragments between components just because they happen to need the same fields today creates artificial dependencies. When one component's requirements change, the shared fragment must be updated, causing all components using it to over-fetch data they don't need.\n\n```tsx\n// ✅ Good: Each component has its own fragment\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n    }\n  `;\n}\n\n// If UserCard later needs 'bio', only UserCard_user changes\n// UserListItem doesn't over-fetch 'bio'\n```\n\n```tsx\n// ❌ Avoid: Sharing a generic fragment across components\nconst COMMON_USER_FIELDS = gql`\n  fragment CommonUserFields on User {\n    id\n    name\n    email\n  }\n`;\n\n// UserCard and UserListItem both use CommonUserFields\n// When UserCard needs 'bio', adding it to CommonUserFields\n// causes UserListItem to over-fetch unnecessarily\n```\n\nThis independence allows each component to evolve its data requirements without affecting unrelated parts of your application.\n\n### One Query Per Page\n\nCompose all page data requirements into a single query at the page level:\n\n```tsx\n// ✅ Good: Single page-level query\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserHeader_user\n        ...UserPosts_user\n        ...UserFriends_user\n      }\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Multiple queries in different components\nfunction UserProfile() {\n  const { data: userData } = useQuery(GET_USER);\n  const { data: postsData } = useQuery(GET_USER_POSTS);\n  const { data: friendsData } = useQuery(GET_USER_FRIENDS);\n  // ...\n}\n```\n\n### Use Fragment-Reading Hooks in Components\n\nNon-page components should use `useFragment` or `useSuspenseFragment`:\n\n```tsx\n// ✅ Good: Component reads fragment data\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n  return <div>{data.name}</div>;\n}\n```\n\n```tsx\n// ❌ Avoid: Component uses query hook\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, { variables: { id: userId } });\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Request Only Required Fields\n\nKeep fragments minimal and only request fields the component actually uses:\n\n```tsx\n// ✅ Good: Only necessary fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Requesting unused fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n      bio\n      friends {\n        id\n        name\n      }\n      posts {\n        id\n        title\n      }\n    }\n  `;\n}\n```\n\n### Use @defer for Below-the-Fold Content\n\nThe `@defer` directive allows you to defer loading of non-critical fields, enabling faster initial page loads by prioritizing essential data. The deferred fields are delivered via incremental delivery and arrive after the non-deferred data, allowing the UI to progressively render as data becomes available.\n\nDefer slow fields that aren't immediately visible:\n\n```tsx\nif (false) {\n  gql`\n    query ProductPage($id: ID!) {\n      product(id: $id) {\n        id\n        name\n        price\n        ...ProductReviews_product @defer\n      }\n    }\n  `;\n}\n```\n\nThis allows the page to render quickly while reviews load in the background.\n\n### Handle Client-Only Fields\n\nUse the `@client` directive for fields resolved locally:\n\n```tsx\nif (false) {\n  gql`\n    fragment TodoItem_todo on Todo {\n      id\n      text\n      completed\n      isSelected @client\n    }\n  `;\n}\n```\n\n### Enable Data Masking for New Applications\n\nAlways enable data masking in new applications:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true,\n});\n```\n\nThis enforces proper boundaries from the start and prevents accidental coupling between components.\n\n## Apollo Client Data Masking vs GraphQL-Codegen Fragment Masking\n\nApollo Client's data masking and GraphQL Code Generator's fragment masking are different features that serve different purposes:\n\n### GraphQL-Codegen Fragment Masking\n\nGraphQL Code Generator's fragment masking (when using the client preset) is a **type-level** feature:\n\n- Masks data only at the TypeScript type level\n- The actual runtime data remains fully accessible on the object\n- Using their `useFragment` hook simply \"unmasks\" the data on a type level\n- Does not prevent accidental access to data at runtime\n- Parent components receive all data and pass it down\n- This means the parent component has to be subscribed to all data\n\n### Apollo Client Data Masking\n\nApollo Client's data masking is a **runtime** feature with significant performance benefits:\n\n- Removes data at the runtime level, not just the type level\n- The `useFragment` and `useSuspenseFragment` hooks create cache subscriptions\n- Parent objects are sparse and only contain unmasked data\n- Prevents accidental access to data that should be masked\n\n### Key Benefits of Apollo Client Data Masking\n\n**1. No Accidental Data Access**\n\nWith runtime data masking, masked fields are not present in the parent object at all. You cannot accidentally access them, even if you bypass TypeScript type checking.\n\n**2. Fewer Re-renders**\n\nApollo Client's approach creates more efficient subscriptions:\n\n- **Without data masking**: Parent component subscribes to all fields (including masked ones). When a masked child field changes, the parent re-renders to pass that runtime data down the tree.\n- **With data masking**: Parent component only subscribes to its own unmasked fields. Subscriptions on masked fields happen lower in the React component tree when the child component calls `useSuspenseFragment`. When a masked field changes, only the child component that subscribed to it re-renders.\n\n### Example\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseQuery, useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction ParentComponent() {\n  const { data } = useSuspenseQuery(GET_USER);\n\n  // With Apollo Client data masking:\n  // - data.user only contains unmasked fields\n  // - Parent doesn't re-render when child-specific fields change\n\n  return <UserCard user={data.user} />;\n}\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a cache subscription specifically for UserCard_user fields\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // Only this component re-renders when these fields change\n  return <div>{data.name}</div>;\n}\n```\n\nThis granular subscription approach improves performance in large applications with deeply nested component trees.\n","docs/agent-skills/apollo-client/references/integration-client.md":"# Apollo Client Integration for Client-Side Apps\n\nThis guide covers setting up Apollo Client in client-side React applications without server-side rendering (SSR). This includes applications using Vite, Parcel, Create React App, or other bundlers that don't implement SSR.\n\nFor applications with SSR, use one of the framework-specific integration guides instead:\n\n- [Next.js App Router](integration-nextjs.md)\n- [React Router Framework Mode](integration-react-router.md)\n- [TanStack Start](integration-tanstack-start.md)\n\n## Installation\n\n```bash\nnpm install @apollo/client graphql rxjs\n```\n\n## TypeScript Code Generation (optional but recommended)\n\nFor type-safe GraphQL operations with TypeScript, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup Steps\n\n### Step 1: Create Client\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\n\n// Recommended: Use HttpOnly cookies for authentication\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  credentials: \"include\", // Sends cookies with requests (secure when using HttpOnly cookies)\n});\n\nconst client = new ApolloClient({\n  link: httpLink,\n  cache: new InMemoryCache(),\n});\n```\n\nIf you need manual token management (less secure, only when HttpOnly cookies aren't available):\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\n\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n});\n\nconst authLink = new SetContextLink(({ headers }) => {\n  const token = localStorage.getItem(\"token\");\n  return {\n    headers: {\n      ...headers,\n      authorization: token ? `Bearer ${token}` : \"\",\n    },\n  };\n});\n\nconst client = new ApolloClient({\n  link: authLink.concat(httpLink),\n  cache: new InMemoryCache(),\n});\n```\n\n### Step 2: Setup Provider\n\n```tsx\nimport { ApolloProvider } from \"@apollo/client\";\nimport App from \"./App\";\n\nfunction Root() {\n  return (\n    <ApolloProvider client={client}>\n      <App />\n    </ApolloProvider>\n  );\n}\n```\n\n### Step 3: Execute Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USERS = gql`\n  query GetUsers {\n    users {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserList() {\n  const { loading, error, data, dataState } = useQuery(GET_USERS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Basic Query Usage\n\n### Using Variables\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n> **Note for TypeScript users**: Use [`dataState`](https://www.apollographql.com/docs/react/data/typescript#type-narrowing-data-with-datastate) for more robust type safety and better type narrowing in Apollo Client 4.x.\n\n### TypeScript Integration\n\nFor complete examples with loading, error handling, and `dataState` for type narrowing, see [Basic Query Usage](#basic-query-usage) above.\n\n#### Usage with Generated Types\n\nFor type-safe operations with code generation, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\nQuick example:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\n// Define your query with the if (false) pattern for code generation\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n#### Usage with Manual Type Annotations\n\nIf not using code generation, define types alongside your queries using `TypedDocumentNode`:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetUserData {\n  user: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface GetUserVariables {\n  id: string;\n}\n\n// Types should always be defined via TypedDocumentNode alongside your queries/mutations, not at the useQuery/useMutation call site\nconst GET_USER: TypedDocumentNode<GetUserData, GetUserVariables> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // data.user is automatically typed from GET_USER\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Basic Mutation Usage\n\n```tsx\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\ninterface CreateUserMutation {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserMutationVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<\n  CreateUserMutation,\n  CreateUserMutationVariables\n> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (formData: FormData) => {\n    const { data } = await createUser({\n      variables: {\n        input: {\n          name: formData.get(\"name\") as string,\n          email: formData.get(\"email\") as string,\n        },\n      },\n    });\n    if (data) {\n      console.log(\"Created user:\", data.createUser);\n    }\n  };\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <input name=\"name\" placeholder=\"Name\" />\n      <input name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create User\"}\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n## Client Configuration Options\n\n```typescript\nconst client = new ApolloClient({\n  // Required: The cache implementation\n  cache: new InMemoryCache({\n    typePolicies: {\n      Query: {\n        fields: {\n          // Field-level cache configuration\n        },\n      },\n    },\n  }),\n\n  // Network layer\n  link: new HttpLink({ uri: \"/graphql\" }),\n\n  // Avoid defaultOptions if possible as they break TypeScript expectations.\n  // Configure options per-query/mutation instead for better type safety.\n  // defaultOptions: {\n  //   watchQuery: { fetchPolicy: 'cache-and-network' },\n  // },\n\n  // DevTools are enabled by default in development\n  // Only configure when enabling in production\n  devtools: {\n    enabled: true, // Only needed for production\n  },\n\n  // Custom name for this client instance\n  clientAwareness: {\n    name: \"web-client\",\n    version: \"1.0.0\",\n  },\n});\n```\n\n## Important Considerations\n\n1. **Choose Your Hook Strategy:** Decide if your application should be based on Suspense. If it is, use suspenseful hooks like `useSuspenseQuery` (see [Suspense Hooks guide](suspense-hooks.md)), otherwise use non-suspenseful hooks like `useQuery` (see [Queries guide](queries.md)).\n\n2. **Client-Side Only:** This setup is for client-side apps without SSR. The Apollo Client instance is created once and reused throughout the application lifecycle.\n\n3. **Authentication:** Prefer HttpOnly cookies with `credentials: \"include\"` in `HttpLink` options to avoid exposing tokens to JavaScript. If manual token management is necessary, use `SetContextLink` to dynamically add authentication headers from `localStorage` or other client-side storage.\n\n4. **Environment Variables:** Store your GraphQL endpoint URL in environment variables for different environments (development, staging, production).\n\n5. **Error Handling:** Always handle `loading` and `error` states when using `useQuery` or `useLazyQuery`. For Suspense-based hooks (`useSuspenseQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n","docs/agent-skills/apollo-client/references/integration-nextjs.md":"# Apollo Client Integration with Next.js App Router\n\nThis guide covers integrating Apollo Client in a Next.js application using the App Router architecture with support for both React Server Components (RSC) and Client Components.\n\n## What is supported?\n\n### React Server Components\n\nApollo Client provides a shared client instance across all server components for a single request, preventing duplicate GraphQL requests and optimizing server-side rendering.\n\n### React Client Components\n\nWhen using the `app` directory, client components are rendered both on the server (SSR) and in the browser. Apollo Client enables you to execute GraphQL queries on the server and use the results to hydrate your browser-side cache, delivering fully-rendered pages to users.\n\n## Installation\n\nInstall Apollo Client and the Next.js integration package:\n\n```bash\nnpm install @apollo/client@latest @apollo/client-integration-nextjs graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup for React Server Components (RSC)\n\n### Step 1: Create Apollo Client Configuration\n\nCreate an `ApolloClient.ts` file in your app directory:\n\n```typescript\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  registerApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nexport const { getClient, query, PreloadQuery } = registerApolloClient(() => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({\n      // Use an absolute URL for SSR (relative URLs cannot be used in SSR)\n      uri: \"https://your-api.com/graphql\",\n      fetchOptions: {\n        // Optional: Next.js-specific fetch options for caching and revalidation\n        // See: https://nextjs.org/docs/app/api-reference/functions/fetch\n      },\n    }),\n  });\n});\n```\n\n### Step 2: Use in Server Components\n\nYou can now use the `getClient` function or the `query` shortcut in your server components:\n\n```typescript\nimport { query } from \"./ApolloClient\";\n\nasync function UserProfile({ userId }: { userId: string }) {\n  const { data } = await query({\n    query: GET_USER,\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Override Next.js Fetch Options\n\nYou can override Next.js-specific `fetch` options per query using `context.fetchOptions`:\n\n```typescript\nconst { data } = await getClient().query({\n  query: GET_USER,\n  context: {\n    fetchOptions: {\n      next: { revalidate: 60 }, // Revalidate every 60 seconds\n    },\n  },\n});\n```\n\n## Setup for Client Components (SSR and Browser)\n\n### Step 1: Create Apollo Wrapper Component\n\nCreate `app/ApolloWrapper.tsx`:\n\n```typescript\n\"use client\";\n\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  ApolloNextAppProvider,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nfunction makeClient() {\n  const httpLink = new HttpLink({\n    // Use an absolute URL for SSR\n    uri: \"https://your-api.com/graphql\",\n    fetchOptions: {\n      // Optional: Next.js-specific fetch options\n      // Note: This doesn't work with `export const dynamic = \"force-static\"`\n    },\n  });\n\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: httpLink,\n  });\n}\n\nexport function ApolloWrapper({ children }: React.PropsWithChildren) {\n  return (\n    <ApolloNextAppProvider makeClient={makeClient}>\n      {children}\n    </ApolloNextAppProvider>\n  );\n}\n```\n\n### Step 2: Wrap Root Layout\n\nWrap your `RootLayout` in the `ApolloWrapper` component in `app/layout.tsx`:\n\n```typescript\nimport { ApolloWrapper } from \"./ApolloWrapper\";\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <ApolloWrapper>{children}</ApolloWrapper>\n      </body>\n    </html>\n  );\n}\n```\n\n> **Note:** This works even if your layout is a React Server Component. It ensures all Client Components share the same Apollo Client instance through `ApolloNextAppProvider`.\n\n### Step 3: Use Apollo Client Hooks in Client Components\n\nFor optimal streaming SSR, use suspense-enabled hooks like `useSuspenseQuery` and `useFragment`:\n\n```typescript\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Preloading Data from RSC to Client Components\n\nYou can preload data in React Server Components to populate the cache of your Client Components.\n\n### Step 1: Use PreloadQuery in Server Components\n\n```tsx\nimport { PreloadQuery } from \"./ApolloClient\";\nimport { Suspense } from \"react\";\n\nexport default async function Page() {\n  return (\n    <PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n      <Suspense fallback={<>Loading...</>}>\n        <ClientChild />\n      </Suspense>\n    </PreloadQuery>\n  );\n}\n```\n\n### Step 2: Consume with useSuspenseQuery in Client Components\n\n```tsx\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function ClientChild() {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n> **Important:** Data fetched this way should be considered client data and never referenced in Server Components. `PreloadQuery` prevents mixing server data and client data by creating a separate `ApolloClient` instance.\n\n### Using with useReadQuery\n\nFor advanced use cases, you can use `PreloadQuery` with `useReadQuery` to avoid request waterfalls:\n\n```tsx\n<PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n  {(queryRef) => (\n    <Suspense fallback={<>Loading...</>}>\n      <ClientChild queryRef={queryRef} />\n    </Suspense>\n  )}\n</PreloadQuery>\n```\n\nIn your Client Component:\n\n```tsx\n\"use client\";\n\nimport {\n  useQueryRefHandlers,\n  useReadQuery,\n  QueryRef,\n} from \"@apollo/client/react\";\n\nexport function ClientChild({ queryRef }: { queryRef: QueryRef<TQueryData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Handling Multipart Responses (@defer) in SSR\n\nWhen using the `@defer` directive, `useSuspenseQuery` will only suspend until the initial response is received. To handle deferred data properly, you have three strategies:\n\n### Strategy 1: Use PreloadQuery with useReadQuery\n\n`PreloadQuery` allows deferred data to be fully transported and streamed chunk-by-chunk.\n\n### Strategy 2: Remove @defer Fragments\n\nUse `RemoveMultipartDirectivesLink` to strip `@defer` directives from queries during SSR:\n\n```typescript\nimport { RemoveMultipartDirectivesLink } from \"@apollo/client-integration-nextjs\";\n\nnew RemoveMultipartDirectivesLink({\n  stripDefer: true, // Default: true\n});\n```\n\nYou can exclude specific fragments from stripping by labeling them:\n\n```graphql\nquery myQuery {\n  fastField\n  ... @defer(label: \"SsrDontStrip1\") {\n    slowField1\n  }\n}\n```\n\n### Strategy 3: Wait for Deferred Data\n\nUse `AccumulateMultipartResponsesLink` to debounce the initial response:\n\n```typescript\nimport { AccumulateMultipartResponsesLink } from \"@apollo/client-integration-nextjs\";\n\nnew AccumulateMultipartResponsesLink({\n  cutoffDelay: 100, // Wait up to 100ms for incremental data\n});\n```\n\n### Combined Approach: SSRMultipartLink\n\nCombine both strategies with `SSRMultipartLink`:\n\n```typescript\nimport { SSRMultipartLink } from \"@apollo/client-integration-nextjs\";\n\nnew SSRMultipartLink({\n  stripDefer: true,\n  cutoffDelay: 100,\n});\n```\n\n## Testing\n\nReset singleton instances between tests using the `resetApolloClientSingletons` helper:\n\n```typescript\nimport { resetApolloClientSingletons } from \"@apollo/client-integration-nextjs\";\n\nafterEach(resetApolloClientSingletons);\n```\n\n## Debugging\n\nEnable verbose logging in your `app/ApolloWrapper.tsx`:\n\n```typescript\nimport { setLogVerbosity } from \"@apollo/client\";\n\nsetLogVerbosity(\"debug\");\n```\n\n## Important Considerations\n\n1. **Separate RSC and SSR Queries:** Avoid overlapping queries between RSC and SSR. RSC queries don't update in the browser, while SSR queries can update dynamically as the cache changes.\n\n2. **Use Absolute URLs:** Always use absolute URLs in `HttpLink` for SSR, as relative URLs cannot be used in server-side rendering.\n\n3. **Streaming SSR:** For optimal performance, use `useSuspenseQuery` and `useFragment` to take advantage of React 18's streaming SSR capabilities.\n\n4. **Suspense Boundaries:** Place `Suspense` boundaries at meaningful places in your UI for the best user experience.\n","docs/agent-skills/apollo-client/references/integration-react-router.md":"# Apollo Client Integration with React Router Framework Mode\n\nThis guide covers integrating Apollo Client in a React Router 7 application with support for modern streaming SSR.\n\n## Installation\n\nInstall Apollo Client and the React Router integration package:\n\n```bash\nnpm install @apollo/client-integration-react-router @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Create Apollo Configuration\n\nCreate an `app/apollo.ts` file that exports a `makeClient` function and an `apolloLoader`:\n\n```typescript\nimport { HttpLink, InMemoryCache } from \"@apollo/client\";\nimport {\n  createApolloLoaderHandler,\n  ApolloClient,\n} from \"@apollo/client-integration-react-router\";\n\n// `request` will be available on the server during SSR or in loaders, but not in the browser\nexport const makeClient = (request?: Request) => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n};\n\nexport const apolloLoader = createApolloLoaderHandler(makeClient);\n```\n\n> **Important:** `ApolloClient` must be imported from `@apollo/client-integration-react-router`, not from `@apollo/client`.\n\n### Step 2: Reveal Entry Files\n\nRun the following command to create the entry files if they don't exist:\n\n```bash\nnpx react-router reveal\n```\n\nThis will create `app/entry.client.tsx` and `app/entry.server.tsx`.\n\n### Step 3: Configure Client Entry\n\nAdjust `app/entry.client.tsx` to wrap your app in `ApolloProvider`:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\nimport { StrictMode, startTransition } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nstartTransition(() => {\n  const client = makeClient();\n  hydrateRoot(\n    document,\n    <StrictMode>\n      <ApolloProvider client={client}>\n        <HydratedRouter />\n      </ApolloProvider>\n    </StrictMode>\n  );\n});\n```\n\n### Step 4: Configure Server Entry\n\nAdjust `app/entry.server.tsx` to wrap your app in `ApolloProvider` during SSR:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\n// ... other imports\n\nexport default function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  routerContext: EntryContext\n) {\n  return new Promise((resolve, reject) => {\n    // ... existing code\n\n    const client = makeClient(request);\n\n    const { pipe, abort } = renderToPipeableStream(\n      <ApolloProvider client={client}>\n        <ServerRouter\n          context={routerContext}\n          url={request.url}\n          abortDelay={ABORT_DELAY}\n        />\n      </ApolloProvider>,\n      {\n        [readyOption]() {\n          shellRendered = true;\n          // ... rest of the handler\n        },\n        // ... other options\n      }\n    );\n  });\n}\n```\n\n### Step 5: Add Hydration Helper\n\nAdd `<ApolloHydrationHelper>` to `app/root.tsx`:\n\n```typescript\nimport { ApolloHydrationHelper } from \"@apollo/client-integration-react-router\";\nimport { Links, Meta, Outlet, Scripts, ScrollRestoration } from \"react-router\";\n\nexport function Layout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <Meta />\n        <Links />\n      </head>\n      <body>\n        <ApolloHydrationHelper>{children}</ApolloHydrationHelper>\n        <ScrollRestoration />\n        <Scripts />\n      </body>\n    </html>\n  );\n}\n\nexport default function App() {\n  return <Outlet />;\n}\n```\n\n## Usage\n\n### Using apolloLoader with useReadQuery\n\nYou can now use the `apolloLoader` function to create Apollo-enabled loaders for your routes:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef } = useLoaderData<typeof loader>();\n  const { data } = useReadQuery(userQueryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n> **Important:** To provide better TypeScript support, `apolloLoader` is a method that you need to call twice: `apolloLoader<LoaderArgs>()(loader)`\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const postsQueryRef = preloadQuery(GET_POSTS, {\n    variables: { userId: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n    postsQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef, postsQueryRef } = useLoaderData<typeof loader>();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: postsData } = useReadQuery(postsQueryRef);\n\n  return (\n    <div>\n      <h1>{userData.user.name}</h1>\n      <h2>Posts</h2>\n      <ul>\n        {postsData.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import ApolloClient from Integration Package:** Always import `ApolloClient` from `@apollo/client-integration-react-router`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **TypeScript Support:** The `apolloLoader` function requires double invocation for proper TypeScript type inference: `apolloLoader<LoaderArgs>()(loader)`.\n\n3. **Request Context:** The `makeClient` function receives the `Request` object during SSR and in loaders, but not in the browser. Use this to set up auth headers or other request-specific configuration.\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Hydration:** The `ApolloHydrationHelper` component ensures that data loaded on the server is properly hydrated on the client, preventing unnecessary refetches.\n","docs/agent-skills/apollo-client/references/integration-tanstack-start.md":"# Apollo Client Integration with TanStack Start\n\nThis guide covers integrating Apollo Client in a TanStack Start application with support for modern streaming SSR.\n\n> **Note:** When using `npx create-tsrouter-app` to create a new TanStack Start application, you can choose Apollo Client in the setup wizard to have all of this configuration automatically set up for you.\n\n## Installation\n\nInstall Apollo Client and the TanStack Start integration package:\n\n```bash\nnpm install @apollo/client-integration-tanstack-start @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Configure Root Route with Context\n\nIn your `routes/__root.tsx`, change from `createRootRoute` to `createRootRouteWithContext` to provide the right context type:\n\n```typescript\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\nimport {\n  createRootRouteWithContext,\n  Outlet,\n} from \"@tanstack/react-router\";\n\nexport const Route = createRootRouteWithContext<ApolloClientIntegration.RouterContext>()({\n  component: RootComponent,\n});\n\nfunction RootComponent() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Outlet />\n      </body>\n    </html>\n  );\n}\n```\n\n### Step 2: Set Up Apollo Client in Router\n\nIn your `router.tsx`, set up your Apollo Client instance and run `routerWithApolloClient`:\n\n```typescript\nimport {\n  routerWithApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** `ApolloClient` and `InMemoryCache` must be imported from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`.\n\n## Usage\n\n### Option 1: Loader with preloadQuery and useReadQuery\n\nUse the `preloadQuery` function in your route loader to preload data during navigation:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/user/$userId\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery }, params }) => {\n    const queryRef = preloadQuery(GET_USER, {\n      variables: { id: params.userId },\n    });\n\n    return {\n      queryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { queryRef } = Route.useLoaderData();\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Option 2: Direct useSuspenseQuery in Component\n\nYou can also use Apollo Client's suspenseful hooks directly in your component without a loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_POSTS: TypedDocumentNode<{\n  posts: Array<{ id: string; title: string; content: string }>;\n}> = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n      content\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/posts\")({\n  component: RouteComponent,\n});\n\nfunction RouteComponent() {\n  const { data } = useSuspenseQuery(GET_POSTS);\n\n  return (\n    <div>\n      <h1>Posts</h1>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>\n            <h2>{post.title}</h2>\n            <p>{post.content}</p>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const Route = createFileRoute(\"/dashboard\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery } }) => {\n    const userQueryRef = preloadQuery(GET_USER, {\n      variables: { id: \"current\" },\n    });\n\n    const statsQueryRef = preloadQuery(GET_STATS, {\n      variables: { period: \"month\" },\n    });\n\n    return {\n      userQueryRef,\n      statsQueryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { userQueryRef, statsQueryRef } = Route.useLoaderData();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: statsData } = useReadQuery(statsQueryRef);\n\n  return (\n    <div>\n      <h1>Welcome, {userData.user.name}</h1>\n      <div>\n        <h2>Monthly Stats</h2>\n        <p>Views: {statsData.stats.views}</p>\n        <p>Clicks: {statsData.stats.clicks}</p>\n      </div>\n    </div>\n  );\n}\n```\n\n### Using useQueryRefHandlers for Refetching\n\nWhen using `useReadQuery`, you can get refetch functionality from `useQueryRefHandlers`:\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n```typescript\nimport { useReadQuery, useQueryRefHandlers, QueryRef } from \"@apollo/client/react\";\n\nfunction UserComponent({ queryRef }: { queryRef: QueryRef<GetUserQuery> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <button onClick={() => refetch()}>Refresh</button>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import from Integration Package:** Always import `ApolloClient` and `InMemoryCache` from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **Context Type:** Use `createRootRouteWithContext<ApolloClientIntegration.RouterContext>()` to provide proper TypeScript types for the `preloadQuery` function in loaders.\n\n3. **Loader vs Component Queries:**\n\n   - Use `preloadQuery` in loaders when you want to start fetching data before the component renders\n   - Use `useSuspenseQuery` directly in components for simpler cases or when data fetching can wait until render\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Management:** The Apollo Client instance is shared across all routes, so cache updates from one route will be reflected in all routes that use the same data.\n\n6. **Authentication:** Use Apollo Client's `SetContextLink` for dynamic auth tokens.\n\n## Advanced Configuration\n\n### Adding Authentication\n\nFor authentication in TanStack Start with SSR support, you need to handle both server and client environments differently. Use `createIsomorphicFn` to provide environment-specific implementations:\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n  routerWithApolloClient,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { ApolloLink, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\nimport { createIsomorphicFn } from \"@tanstack/react-start\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { getSession, getCookie } from \"@tanstack/react-start/server\";\nimport { routeTree } from \"./routeTree.gen\";\n\n// Create isomorphic link that uses different implementations per environment\nconst createAuthLink = createIsomorphicFn()\n  .server(() => {\n    // Server-only: Can access server-side functions like `getCookies`, `getCookie`, `getSession`, etc. exported from `\"@tanstack/react-start/server\"`\n    return new SetContextLink(async (prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: getCookie(\"Authorization\"),\n        },\n      };\n    });\n  })\n  .client(() => {\n    // Client-only: Can access `localStorage` or other browser APIs\n    return new SetContextLink((prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: localStorage.getItem(\"authToken\") ?? \"\",\n        },\n      };\n    });\n  });\n\nexport function getRouter() {\n  const httpLink = new HttpLink({\n    uri: \"https://your-graphql-endpoint.com/graphql\",\n  });\n\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: ApolloLink.from([createAuthLink(), httpLink]),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** The `getRouter` function is called both on the server and client, so it must not contain environment-specific code. Use `createIsomorphicFn` to provide different implementations:\n>\n> - **Server:** Can access server-only functions like `getSession`, `getCookies`, `getCookie` from `@tanstack/react-start/server` to access authentication information in request or session data\n> - **Client:** Can use `localStorage` or other browser APIs to access auth tokens (if setting `credentials: \"include\"` is sufficient, try to prefer that over manually setting auth headers client-side)\n>\n> This ensures your authentication works correctly in both SSR and browser contexts.\n\n### Custom Cache Configuration\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\nimport { routerWithApolloClient } from \"@apollo/client-integration-tanstack-start\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache({\n      typePolicies: {\n        Query: {\n          fields: {\n            posts: {\n              merge(existing = [], incoming) {\n                return [...existing, ...incoming];\n              },\n            },\n          },\n        },\n      },\n    }),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n","docs/agent-skills/apollo-client/references/mutations.md":"# Mutations Reference\n\n## Table of Contents\n\n- [useMutation Hook](#usemutation-hook)\n- [Mutation Variables](#mutation-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [Optimistic UI](#optimistic-ui)\n- [Cache Updates](#cache-updates)\n- [Refetch Queries](#refetch-queries)\n- [Error Handling](#error-handling)\n\n## useMutation Hook\n\nThe `useMutation` hook is used to execute GraphQL mutations.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst ADD_TODO = gql`\n  mutation AddTodo($text: String!) {\n    addTodo(text: $text) {\n      id\n      text\n      completed\n    }\n  }\n`;\n\nfunction AddTodo() {\n  const [addTodo, { data, loading, error }] = useMutation(ADD_TODO);\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        const form = e.currentTarget;\n        const text = new FormData(form).get(\"text\") as string;\n        addTodo({ variables: { text } });\n        form.reset();\n      }}\n    >\n      <input name=\"text\" placeholder=\"Add todo\" />\n      <button type=\"submit\" disabled={loading}>\n        Add\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n### Return Tuple\n\n```typescript\nconst [\n  mutateFunction, // Function to call to execute mutation\n  {\n    data, // Mutation result data\n    loading, // True while mutation is in flight\n    error, // ApolloError if mutation failed\n    called, // True if mutation has been called\n    reset, // Reset mutation state\n    client, // Apollo Client instance\n  },\n] = useMutation(MUTATION);\n```\n\n## Mutation Variables\n\n### Variables in Options\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  variables: {\n    input: {\n      name: \"Default User\",\n      email: \"default@example.com\",\n    },\n  },\n});\n\n// Call with default variables\nawait createUser();\n\n// Override variables\nawait createUser({\n  variables: {\n    input: {\n      name: \"Custom User\",\n      email: \"custom@example.com\",\n    },\n  },\n});\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\n\ninterface CreateUserData {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<CreateUserData, CreateUserVariables> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nconst [createUser, { data, loading }] = useMutation(CREATE_USER);\n\nconst { data } = await createUser({\n  variables: {\n    input: { name: \"John\", email: \"john@example.com\" },\n  },\n});\n\n// data.createUser is fully typed\n```\n\n## Loading and Error States\n\n### Handling in UI\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading, error, data, reset }] =\n    useMutation(CREATE_POST);\n\n  if (data) {\n    return (\n      <div>\n        <p>Post created: {data.createPost.title}</p>\n        <button onClick={reset}>Create another</button>\n      </div>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <input name=\"title\" disabled={loading} />\n      <textarea name=\"content\" disabled={loading} />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n      {error && (\n        <div className=\"error\">\n          <p>Failed to create post: {error.message}</p>\n          <button onClick={reset}>Try again</button>\n        </div>\n      )}\n    </form>\n  );\n}\n```\n\n### Async/Await Pattern\n\nIf you only need the promise without using the hook's loading/data state, use `client.mutate` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction CreatePost() {\n  const client = useApolloClient();\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await client.mutate({\n        mutation: CREATE_POST,\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      ...\n    </form>\n  );\n}\n```\n\nIf you do use the hook's state, e.g. because you want to render the `loading` state, errors or returned `data`, you can also use the `useMutation` hook with `async..await` in your handler:\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading }] = useMutation(CREATE_POST);\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await createPost({\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n    </form>\n  );\n}\n```\n\n## Optimistic UI\n\nOptimistic UI immediately reflects the expected result of a mutation before the server responds.\n\n### Basic Optimistic Response\n\n**Important**: `optimisticResponse` needs to be a full valid response for the mutation. A partial result might result in subtle errors.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  optimisticResponse: {\n    addTodo: {\n      __typename: \"Todo\",\n      id: \"temp-id\",\n      text: \"New todo\",\n      completed: false,\n    },\n  },\n});\n```\n\n### Dynamic Optimistic Response\n\n```tsx\nfunction TodoList() {\n  const [addTodo] = useMutation(ADD_TODO);\n\n  const handleAdd = (text: string) => {\n    addTodo({\n      variables: { text },\n      optimisticResponse: {\n        addTodo: {\n          __typename: \"Todo\",\n          id: `temp-${Date.now()}`,\n          text,\n          completed: false,\n        },\n      },\n    });\n  };\n\n  return <AddTodoForm onAdd={handleAdd} />;\n}\n```\n\n### Optimistic Response with Cache Update\n\n```tsx\nconst [toggleTodo] = useMutation(TOGGLE_TODO, {\n  optimisticResponse: ({ id }) => ({\n    toggleTodo: {\n      __typename: \"Todo\",\n      id,\n      completed: true, // Assume success\n    },\n  }),\n  update: (cache, { data }) => {\n    // This runs twice: once with optimistic data, once with server data\n    cache.modify({\n      id: cache.identify(data.toggleTodo),\n      fields: {\n        completed: () => data.toggleTodo.completed,\n      },\n    });\n  },\n});\n```\n\n## Cache Updates\n\n### Using update Function\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Read existing todos from cache\n    const existingTodos = cache.readQuery<{ todos: Todo[] }>({\n      query: GET_TODOS,\n    });\n\n    // Write updated list back to cache\n    cache.writeQuery({\n      query: GET_TODOS,\n      data: {\n        todos: [...(existingTodos?.todos ?? []), data.addTodo],\n      },\n    });\n  },\n});\n```\n\n### cache.modify\n\n```tsx\nconst [deleteTodo] = useMutation(DELETE_TODO, {\n  update: (cache, { data }) => {\n    cache.modify({\n      fields: {\n        todos: (existingTodos: Reference[], { readField }) => {\n          return existingTodos.filter(\n            (todoRef) => readField(\"id\", todoRef) !== data.deleteTodo.id\n          );\n        },\n      },\n    });\n  },\n});\n```\n\n### cache.evict\n\n```tsx\nconst [deleteUser] = useMutation(DELETE_USER, {\n  update: (cache, { data }) => {\n    // Remove the user object from cache entirely\n    cache.evict({ id: cache.identify(data.deleteUser) });\n    // Clean up dangling references\n    cache.gc();\n  },\n});\n```\n\n### Updating Related Queries\n\n```tsx\nconst [createPost] = useMutation(CREATE_POST, {\n  update: (cache, { data }) => {\n    // Update author's post count\n    cache.modify({\n      id: cache.identify({ __typename: \"User\", id: data.createPost.authorId }),\n      fields: {\n        postCount: (existing) => existing + 1,\n        posts: (existing, { toReference }) => [\n          ...existing,\n          toReference(data.createPost),\n        ],\n      },\n    });\n\n    // Add to feed\n    cache.modify({\n      fields: {\n        feed: (existing, { toReference }) => [\n          toReference(data.createPost),\n          ...existing,\n        ],\n      },\n    });\n  },\n});\n```\n\n## Refetch Queries\n\n### Basic Refetch\n\nThere are three refetch notations:\n\n- **String**: `refetchQueries: ['getTodos']` - refetches all active `getTodos` queries\n- **Query document**: `refetchQueries: [GET_TODOS]` - refetches all active queries using this document\n- **Object**: `refetchQueries: [{ query: GET_TODOS }, { query: GET_TODOS, variables: { page: 25 } }]` - **fetches** the query, regardless if it's actively used in the UI\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  // Refetch all active GET_TODOS queries\n  refetchQueries: [\"getTodos\"],\n  // Or: refetchQueries: [GET_TODOS],\n});\n\n// Fetch specific query with variables (even if not active)\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }, { query: GET_TODO_COUNT }],\n});\n```\n\n### Conditional Refetch\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: (result) => {\n    if (result.data?.addTodo.priority === \"HIGH\") {\n      return [{ query: GET_HIGH_PRIORITY_TODOS }];\n    }\n    return [{ query: GET_TODOS }];\n  },\n});\n```\n\n### Refetch Active Queries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: \"active\", // Refetch all active queries\n  // Or: 'all' to refetch all queries (including inactive)\n});\n```\n\n### awaitRefetchQueries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }],\n  awaitRefetchQueries: true, // Wait for refetch before resolving mutation\n});\n```\n\n### onQueryUpdated\n\nReturning `true` from `onQueryUpdated` causes a refetch. Don't call `refetch()` manually inside `onQueryUpdated`, as it won't retain the query and might cancel it early.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Update cache...\n  },\n  onQueryUpdated: (observableQuery) => {\n    // Called for each query affected by cache update\n    console.log(`Query ${observableQuery.queryName} was updated`);\n    // Return true to refetch\n    return true;\n  },\n});\n```\n\n## Error Handling\n\n### Error Policy\n\n```tsx\nconst [createUser, { loading }] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\", // Return both data and errors\n});\n\nconst { data, errors } = await createUser({\n  variables: { input },\n});\n\n// Handle partial success\nif (data?.createUser) {\n  console.log(\"User created:\", data.createUser);\n}\nif (errors) {\n  console.warn(\"Some errors occurred:\", errors);\n}\n```\n\n### onError Callback\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  onError: (error) => {\n    // Handle error globally\n    toast.error(`Failed to create user: ${error.message}`);\n\n    // Log to error tracking service\n    Sentry.captureException(error);\n  },\n  onCompleted: (data) => {\n    toast.success(`User ${data.createUser.name} created!`);\n  },\n});\n```\n\n### Field-Level Errors\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\",\n});\n\nconst handleSubmit = async (input: CreateUserInput) => {\n  const { data, errors } = await createUser({\n    variables: { input },\n  });\n\n  // Handle GraphQL validation errors\n  const fieldErrors = errors?.reduce(\n    (acc, error) => {\n      const field = error.extensions?.field as string;\n      if (field) {\n        acc[field] = error.message;\n      }\n      return acc;\n    },\n    {} as Record<string, string>\n  );\n\n  if (fieldErrors?.email) {\n    setEmailError(fieldErrors.email);\n  }\n};\n```\n","docs/agent-skills/apollo-client/references/queries.md":"# Queries Reference\n\n> **Note**: In most applications, there should only be one use of a query hook per page. Use fragment-reading hooks (`useFragment`, `useSuspenseFragment`) with component-colocated fragments and data masking for the rest of the page components.\n\n## Table of Contents\n\n- [useQuery Hook](#usequery-hook)\n- [Query Variables](#query-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [useLazyQuery](#uselazyquery)\n- [Polling and Refetching](#polling-and-refetching)\n- [Fetch Policies](#fetch-policies)\n- [Conditional Queries](#conditional-queries)\n\n## useQuery Hook\n\nThe `useQuery` hook is the primary way to fetch data in Apollo Client in non-suspenseful applications. It returns loading and error states that must be handled.\n\n> **Note**: In suspenseful applications, use `useSuspenseQuery` or `useBackgroundQuery` instead. See the [Suspense Hooks reference](suspense-hooks.md) for more details.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_DOGS = gql`\n  query GetDogs {\n    dogs {\n      id\n      breed\n      displayImage\n    }\n  }\n`;\n\nfunction Dogs() {\n  const { loading, error, data } = useQuery(GET_DOGS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  loading, // True during initial load\n  error, // ApolloError if request failed\n  networkStatus, // Detailed network state (1-8)\n  dataState, // For TypeScript type narrowing (AC 4.x)\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n  startPolling, // Start polling at interval\n  stopPolling, // Stop polling\n  subscribeToMore, // Add subscription to query\n  updateQuery, // Manually update query result\n  client, // Apollo Client instance\n  called, // True if query has been executed\n  previousData, // Previous data (useful during loading)\n} = useQuery(QUERY);\n```\n\n## Query Variables\n\n### Basic Variables\n\n```tsx\nconst GET_DOG = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DogPhoto({ breed }: { breed: string }) {\n  const { loading, error, data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  if (loading) return null;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <img src={data.dog.displayImage} alt={breed} />;\n}\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters for better type safety:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetDogData {\n  dog: {\n    id: string;\n    displayImage: string;\n  };\n}\n\ninterface GetDogVariables {\n  breed: string;\n}\n\nconst GET_DOG: TypedDocumentNode<GetDogData, GetDogVariables> = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_DOG, {\n  variables: { breed: \"bulldog\" },\n});\n\n// data?.dog is fully typed\n```\n\n### Dynamic Variables\n\n```tsx\nfunction DogSelector() {\n  const [breed, setBreed] = useState(\"bulldog\");\n\n  // Query automatically re-runs when breed changes\n  const { data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  return (\n    <select value={breed} onChange={(e) => setBreed(e.target.value)}>\n      <option value=\"bulldog\">Bulldog</option>\n      <option value=\"poodle\">Poodle</option>\n    </select>\n  );\n}\n```\n\n## Loading and Error States\n\n### Using Previous Data\n\n```tsx\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, data, previousData } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // Show previous data while loading new data\n  const displayData = data ?? previousData;\n\n  return (\n    <div>\n      {loading && <LoadingSpinner />}\n      {displayData && <UserCard user={displayData.user} />}\n    </div>\n  );\n}\n```\n\n### Network Status\n\n```tsx\nimport { NetworkStatus } from \"@apollo/client\";\n\nfunction Dogs() {\n  const { loading, error, data, networkStatus, refetch } = useQuery(GET_DOGS, {\n    notifyOnNetworkStatusChange: true,\n  });\n\n  if (networkStatus === NetworkStatus.refetch) {\n    return <p>Refetching...</p>;\n  }\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <>\n      <button onClick={() => refetch()}>Refresh</button>\n      <ul>\n        {data.dogs.map((dog) => (\n          <li key={dog.id}>{dog.breed}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n## useLazyQuery\n\nUse `useLazyQuery` when you want to execute a query in response to a user-triggered event (like a button click) rather than on component mount.\n\n**Important**: `useLazyQuery` doesn't guarantee a network request - it only sets variables. If data is already in the cache, this isn't a \"refetch\". Only use `useLazyQuery` if you consume the second tuple value (loading, data, error states) to synchronize cache data with the component. If you only need the promise, use `client.query` directly instead.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useLazyQuery } from \"@apollo/client/react\";\n\nconst GET_DOG_PHOTO = gql`\n  query GetDogPhoto($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DelayedQuery() {\n  const [getDog, { loading, error, data, called }] =\n    useLazyQuery(GET_DOG_PHOTO);\n\n  if (called && loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <div>\n      {data?.dog && <img src={data.dog.displayImage} />}\n      <button onClick={() => getDog({ variables: { breed: \"bulldog\" } })}>\n        Get Bulldog Photo\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use client.query Instead\n\nIf you only need the promise result and don't consume the loading/error/data states from the hook, use `client.query` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction SearchDogs() {\n  const client = useApolloClient();\n  const [search, setSearch] = useState(\"\");\n\n  const handleSearch = async () => {\n    try {\n      const { data } = await client.query({\n        query: SEARCH_DOGS,\n        variables: { query: search },\n      });\n      console.log(\"Found dogs:\", data.searchDogs);\n    } catch (error) {\n      console.error(\"Search failed:\", error);\n    }\n  };\n\n  return (\n    <div>\n      <input value={search} onChange={(e) => setSearch(e.target.value)} />\n      <button onClick={handleSearch}>Search</button>\n    </div>\n  );\n}\n```\n\n## Polling and Refetching\n\n### Polling\n\n```tsx\nfunction LiveFeed() {\n  const { data, startPolling, stopPolling } = useQuery(GET_FEED, {\n    pollInterval: 5000, // Poll every 5 seconds\n  });\n\n  // Or control polling dynamically\n  useEffect(() => {\n    startPolling(5000);\n    return () => stopPolling();\n  }, [startPolling, stopPolling]);\n\n  return <Feed items={data?.feed} />;\n}\n```\n\n### Manual Refetching\n\n```tsx\nfunction DogList() {\n  const { data, refetch } = useQuery(GET_DOGS);\n\n  return (\n    <div>\n      <button onClick={() => refetch()}>Refresh</button>\n      <button onClick={() => refetch({ breed: \"poodle\" })}>\n        Refetch Poodles\n      </button>\n      <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>\n    </div>\n  );\n}\n```\n\n## Fetch Policies\n\nControl how the query interacts with the cache.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-only`        | Only return cached data, never fetch                       |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n| `standby`           | Same as cache-first but doesn't auto-update                |\n\n### Usage Examples\n\n```tsx\n// Real-time data - always fetch\nconst { data } = useQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Static data - prefer cache\nconst { data } = useQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data, loading } = useQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n\n// Fetch once, then use cache\nconst { data } = useQuery(GET_USER_PROFILE, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n```\n\n### nextFetchPolicy\n\n```tsx\n// First request: network-only\n// Subsequent requests: cache-first\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n\n// Or use a function for more control\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: (currentFetchPolicy, { reason, observable }) => {\n    if (reason === \"after-fetch\") {\n      return \"cache-first\";\n    }\n    return currentFetchPolicy;\n  },\n});\n```\n\n## Conditional Queries\n\n### Using skipToken (Recommended)\n\nUse `skipToken` to conditionally skip queries without TypeScript issues:\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n### Skip Option (Alternative)\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId! },\n    skip: !userId, // Don't execute if no userId\n  });\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n> **Note**: Using `skipToken` is preferred over `skip` as it avoids TypeScript issues with required variables and the non-null assertion operator.\n\n### SSR Skip\n\n```tsx\n// Skip during server-side rendering\nconst { data } = useQuery(GET_USER_LOCATION, {\n  skip: typeof window === \"undefined\",\n  ssr: false,\n});\n```\n","docs/agent-skills/apollo-client/references/state-management.md":"# State Management Reference\n\n## Table of Contents\n\n- [Reactive Variables](#reactive-variables)\n- [Local-Only Fields](#local-only-fields)\n- [LocalState Resolvers](#localstate-resolvers)\n- [Combining Remote and Local State](#combining-remote-and-local-state)\n- [useReactiveVar Hook](#usereactivevar-hook)\n\n## Reactive Variables\n\nReactive variables are a way to store local state outside of the Apollo Client cache while still triggering reactive updates.\n\n**Important**: Reactive variables store a single value that notifies `ApolloClient` instances when changed. They do not have separate values per ApolloClient instance. In multi-user environments like SSR, global or module-level reactive variables could be shared between users and cause data leaks. In frameworks that use SSR, always avoid storing reactive variables as globals.\n\n### Creating Reactive Variables\n\n```typescript\nimport { makeVar } from \"@apollo/client\";\n\n// Simple reactive variable\nexport const isLoggedInVar = makeVar<boolean>(false);\n\n// Object reactive variable\nexport const cartItemsVar = makeVar<CartItem[]>([]);\n\n// Complex state\ninterface AppState {\n  theme: \"light\" | \"dark\";\n  sidebarOpen: boolean;\n  notifications: Notification[];\n}\n\nexport const appStateVar = makeVar<AppState>({\n  theme: \"light\",\n  sidebarOpen: true,\n  notifications: [],\n});\n```\n\n### Reading Reactive Variables\n\n```tsx\n// Direct read (non-reactive)\nconst isLoggedIn = isLoggedInVar();\n\n// Reactive read in component\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction AuthButton() {\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return isLoggedIn ?\n      <button onClick={() => isLoggedInVar(false)}>Logout</button>\n    : <button onClick={() => isLoggedInVar(true)}>Login</button>;\n}\n```\n\n### Updating Reactive Variables\n\n```typescript\n// Set new value\nisLoggedInVar(true);\n\n// Update based on current value\ncartItemsVar([...cartItemsVar(), newItem]);\n\n// Update object state\nappStateVar({\n  ...appStateVar(),\n  theme: \"dark\",\n});\n\n// Helper function pattern\nexport function toggleSidebar() {\n  const current = appStateVar();\n  appStateVar({ ...current, sidebarOpen: !current.sidebarOpen });\n}\n\nexport function addNotification(notification: Notification) {\n  const current = appStateVar();\n  appStateVar({\n    ...current,\n    notifications: [...current.notifications, notification],\n  });\n}\n```\n\n## Local-Only Fields\n\nLocal-only fields are fields defined in queries but resolved entirely on the client using the `@client` directive.\n\n**Important**: To use any `@client` fields, you need to add `LocalState` to the `ApolloClient` initialization:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({}),\n  // ... other options\n});\n```\n\n> **Note**: `LocalState` is an Apollo Client 4.x concept and did not exist as a class in previous versions. In previous versions, a `localState` option was not necessary, and local resolvers (if used) could be passed directly to the `ApolloClient` constructor.\n\n### Basic @client Fields\n\n```tsx\nconst GET_USER_WITH_LOCAL = gql`\n  query GetUserWithLocal($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n      # Local-only fields\n      isSelected @client\n      displayName @client\n    }\n  }\n`;\n\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER_WITH_LOCAL, {\n    variables: { id: userId },\n  });\n\n  return (\n    <div className={data?.user.isSelected ? \"selected\" : \"\"}>\n      <h2>{data?.user.displayName}</h2>\n      <p>{data?.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Local Field Read Functions (Type Policies)\n\nLocal field `read` functions are defined in entity-level type policies. You can use reactive variables inside these `read` functions, along with other calculations or derived values:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Simple local field from reactive variable\n        isSelected: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return selectedUsersVar().includes(id);\n          },\n        },\n\n        // Computed local field (derived value)\n        displayName: {\n          read(_, { readField }) {\n            const name = readField(\"name\");\n            const email = readField(\"email\");\n            return name || email?.split(\"@\")[0] || \"Anonymous\";\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## LocalState Resolvers\n\n### Query-Level Local Resolvers\n\nQuery-level local fields can be defined using `LocalState` resolvers. **Note**: Do not read reactive variables inside LocalState resolvers - this is not a documented/tested feature. It might not behave as expected.\n\n```typescript\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Query: {\n        // Read from localStorage\n        theme: () => {\n          if (typeof window !== \"undefined\") {\n            return localStorage.getItem(\"theme\") || \"light\";\n          }\n          return \"light\";\n        },\n\n        // Read from cache\n        currentUser: (_, __, { cache }) => {\n          if (typeof window === \"undefined\") return null;\n          const userId = localStorage.getItem(\"currentUserId\");\n          if (!userId) return null;\n          return cache.readFragment({\n            id: cache.identify({ __typename: \"User\", id: userId }),\n            fragment: gql`\n              fragment CurrentUser on User {\n                id\n                name\n                email\n              }\n            `,\n          });\n        },\n\n        // Compute value\n        isOnline: () => {\n          if (typeof navigator !== \"undefined\") {\n            return navigator.onLine;\n          }\n          return true;\n        },\n      },\n    },\n  }),\n});\n```\n\n### Using Local Query Fields\n\n```tsx\nconst GET_AUTH_STATE = gql`\n  query GetAuthState {\n    isLoggedIn @client\n    currentUser @client {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction AuthStatus() {\n  const { data } = useQuery(GET_AUTH_STATE);\n\n  if (!data?.isLoggedIn) {\n    return <LoginButton />;\n  }\n\n  return <UserMenu user={data.currentUser} />;\n}\n```\n\n## Combining Remote and Local State\n\n### Mixing Remote and Local Fields\n\n```tsx\nconst GET_PRODUCTS = gql`\n  query GetProducts {\n    products {\n      id\n      name\n      price\n      # Local fields\n      quantity @client\n      isInCart @client\n    }\n  }\n`;\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Product: {\n      fields: {\n        quantity: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            const cartItem = cartItemsVar().find(\n              (item) => item.productId === id\n            );\n            return cartItem?.quantity ?? 0;\n          },\n        },\n\n        isInCart: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return cartItemsVar().some((item) => item.productId === id);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Local Mutations\n\n```tsx\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Mutation: {\n        addToCart: (_, { productId, quantity }, { cache }) => {\n          // Read current cart from cache\n          const { cart } = cache.readQuery({ query: GET_CART }) || { cart: [] };\n\n          const existing = cart.find((item) => item.productId === productId);\n\n          const updatedCart =\n            existing ?\n              cart.map((item) =>\n                item.productId === productId ?\n                  { ...item, quantity: item.quantity + quantity }\n                : item\n              )\n            : [...cart, { productId, quantity, __typename: \"CartItem\" }];\n\n          // Write updated cart back to cache\n          cache.writeQuery({\n            query: GET_CART,\n            data: { cart: updatedCart },\n          });\n\n          return true;\n        },\n      },\n    },\n  }),\n});\n\nconst ADD_TO_CART = gql`\n  mutation AddToCart($productId: ID!, $quantity: Int!) {\n    addToCart(productId: $productId, quantity: $quantity) @client\n  }\n`;\n```\n\n### Persisting Local State\n\n```typescript\n// Create a helper function to permanently subscribe to reactive variable changes, without creating memory leaks\nfunction subscribeToVariable<T>(\n  weakRef: WeakRef<ReactiveVar<T>>,\n  listener: ReactiveListener<T>\n) {\n  weakRef.deref()?.onNextChange((value) => {\n    listener(value);\n    subscribeToVariable(weakRef, listener);\n  });\n}\n\n// Create reactive variable with persistence\nconst persistentCartVar = makeVar<CartItem[]>(\n  typeof window !== \"undefined\" && localStorage.getItem(\"cart\") ?\n    JSON.parse(localStorage.getItem(\"cart\")!)\n  : []\n);\n\n// Save to localStorage when reactive variable changes\nsubscribeToVariable(new WeakRef(persistentCartVar), (items) => {\n  try {\n    if (typeof window !== \"undefined\") {\n      localStorage.setItem(\"cart\", JSON.stringify(items));\n    }\n  } catch (error) {\n    console.error(\"Failed to persist cart:\", error);\n  }\n});\n```\n\n## useReactiveVar Hook\n\nThe `useReactiveVar` hook subscribes a component to reactive variable updates.\n\n### Basic Usage\n\n```tsx\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction ThemeToggle() {\n  const theme = useReactiveVar(themeVar);\n\n  return (\n    <button onClick={() => themeVar(theme === \"light\" ? \"dark\" : \"light\")}>\n      Current: {theme}\n    </button>\n  );\n}\n```\n\n### With Derived State\n\n```tsx\nfunction CartSummary() {\n  const cartItems = useReactiveVar(cartItemsVar);\n\n  // Derived values are computed on each render\n  const totalItems = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n  const totalPrice = cartItems.reduce(\n    (sum, item) => sum + item.price * item.quantity,\n    0\n  );\n\n  return (\n    <div>\n      <p>Items: {totalItems}</p>\n      <p>Total: ${totalPrice.toFixed(2)}</p>\n    </div>\n  );\n}\n```\n\n### Multiple Reactive Variables\n\n```tsx\nfunction AppLayout() {\n  const theme = useReactiveVar(themeVar);\n  const sidebarOpen = useReactiveVar(sidebarOpenVar);\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return (\n    <div className={`app ${theme}`}>\n      {isLoggedIn && sidebarOpen && <Sidebar />}\n      <main>\n        <Outlet />\n      </main>\n    </div>\n  );\n}\n```\n","docs/agent-skills/apollo-client/references/suspense-hooks.md":"# Suspense Hooks Reference\n\n> **Note**: Suspense hooks are the recommended approach for data fetching in modern React applications (React 18+). They provide cleaner code, better loading state handling, and enable streaming SSR.\n\n## Table of Contents\n\n- [useSuspenseQuery Hook](#usesuspensequery-hook)\n- [useBackgroundQuery and useReadQuery](#usebackgroundquery-and-usereadquery)\n- [useLoadableQuery](#useloadablequery)\n- [createQueryPreloader](#createquerypreloader)\n- [useQueryRefHandlers](#usequeryrefhandlers)\n- [Distinguishing Queries with queryKey](#distinguishing-queries-with-querykey)\n- [Suspense Boundaries and Error Handling](#suspense-boundaries-and-error-handling)\n- [Transitions](#transitions)\n- [Avoiding Request Waterfalls](#avoiding-request-waterfalls)\n- [Fetch Policies](#fetch-policies)\n- [Streaming SSR or React Server Components](#streaming-ssr-or-react-server-components)\n- [Conditional Queries](#conditional-queries)\n\n## useSuspenseQuery Hook\n\nThe `useSuspenseQuery` hook is the Suspense-ready replacement for `useQuery`. It initiates a network request and causes the component calling it to suspend while the request is made. Unlike `useQuery`, it does not return `loading` states—these are handled by React's Suspense boundaries and error boundaries.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { GET_DOG } from \"./queries.generated\";\n\nfunction App() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Dog id=\"3\" />\n    </Suspense>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  // data is always defined when this component renders\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  dataState, // With default options: \"complete\" | \"streaming\"\n  // With returnPartialData: also \"partial\"\n  // With errorPolicy \"all\" or \"ignore\": also \"empty\"\n  error, // ApolloError (only when errorPolicy is \"all\" or \"ignore\")\n  networkStatus, // NetworkStatus.ready, NetworkStatus.loading, etc.\n  client, // Apollo Client instance\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n} = useSuspenseQuery(QUERY, options);\n```\n\n### Key Differences from useQuery\n\n- **No `loading` boolean**: Component suspends instead of returning `loading: true`\n- **Error handling**: With default `errorPolicy` (`none`), errors are thrown and caught by error boundaries. With `errorPolicy: \"all\"` or `\"ignore\"`, the `error` property is returned and `data` may be `undefined`.\n- **`data` availability**: With default `errorPolicy` (`none`), `data` is guaranteed to be present when the component renders. With `errorPolicy: \"all\"` or `\"ignore\"`, when `dataState` is `empty`, `data` may be `undefined`.\n- **Suspense boundaries**: Must wrap component with `<Suspense>` to handle loading state\n\n### Changing Variables\n\nWhen variables change, `useSuspenseQuery` automatically re-runs the query. If the data is not in the cache, the component suspends again.\n\n```tsx\nimport { useState } from \"react\";\nimport { GET_DOGS } from \"./queries.generated\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => setSelectedDog(e.target.value)}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog.name}</div>\n      <div>Breed: {data.dog.breed}</div>\n    </>\n  );\n}\n```\n\n### Rendering Partial Data\n\nUse `returnPartialData` to render immediately with partial cache data instead of suspending. The component will still suspend if there is no data in the cache.\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    returnPartialData: true,\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog?.name ?? \"Unknown\"}</div>\n      {data.dog?.breed && <div>Breed: {data.dog.breed}</div>}\n    </>\n  );\n}\n```\n\n## useBackgroundQuery and useReadQuery\n\nUse `useBackgroundQuery` with `useReadQuery` to avoid request waterfalls by starting a query in a parent component and reading the result in a child component. This pattern enables the parent to start fetching data before the child component renders.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useBackgroundQuery, useReadQuery } from \"@apollo/client/react\";\n\nfunction Parent() {\n  // Start fetching immediately\n  const [queryRef] = useBackgroundQuery(GET_DOG, {\n    variables: { id: \"3\" },\n  });\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Child queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction Child({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  // Read the query result\n  const { data } = useReadQuery(queryRef);\n\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### When to Use\n\n- **Avoiding waterfalls**: Start fetching data in a parent (preferably above a suspense boundary) before child components render\n- **Preloading data**: Begin fetching before the component that needs the data is ready\n- **Parallel queries**: Start multiple queries at once in a parent component\n\n### Return Values\n\n`useBackgroundQuery` returns a tuple:\n\n```typescript\nconst [\n  queryRef, // QueryRef to pass to useReadQuery\n  { refetch, fetchMore, subscribeToMore }, // Helper functions\n] = useBackgroundQuery(QUERY, options);\n```\n\n`useReadQuery` returns the query result:\n\n```typescript\nconst {\n  data, // Query result data (always defined)\n  dataState, // \"complete\" | \"streaming\" | \"partial\" | \"empty\"\n  error, // ApolloError (if errorPolicy allows)\n  networkStatus, // Detailed network state (1-8)\n} = useReadQuery(queryRef);\n```\n\n## useLoadableQuery\n\nUse `useLoadableQuery` to imperatively load a query in response to a user interaction (like a button click) instead of on component mount.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useLoadableQuery, useReadQuery } from \"@apollo/client/react\";\nimport { GET_GREETING } from \"./queries.generated\";\n\nfunction App() {\n  const [loadGreeting, queryRef] = useLoadableQuery(GET_GREETING);\n\n  return (\n    <>\n      <button\n        onClick={() => loadGreeting({ variables: { language: \"english\" } })}\n      >\n        Load Greeting\n      </button>\n      <Suspense fallback={<div>Loading...</div>}>\n        {queryRef && <Greeting queryRef={queryRef} />}\n      </Suspense>\n    </>\n  );\n}\n\nfunction Greeting({ queryRef }: { queryRef: QueryRef<GreetingData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.greeting.message}</div>;\n}\n```\n\n### Return Values\n\n```typescript\nconst [\n  loadQuery, // Function to load the query\n  queryRef, // QueryRef (null until loadQuery is called)\n  { refetch, fetchMore, subscribeToMore, reset }, // Helper functions\n] = useLoadableQuery(QUERY, options);\n```\n\n### When to Use\n\n- **User-triggered fetching**: Load data in response to user actions\n- **Lazy loading**: Defer data fetching until it's actually needed\n- **Progressive disclosure**: Load data for UI elements that may not be initially visible\n\n## createQueryPreloader\n\nThe `createQueryPreloader` function creates a `preloadQuery` function that can be used to initiate queries outside of React components. This is useful for preloading data before a component renders, such as in route loaders or event handlers.\n\n### Basic Usage\n\n```tsx\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createQueryPreloader } from \"@apollo/client/react\";\n\nconst client = new ApolloClient({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  cache: new InMemoryCache(),\n});\n\n// Create a preload function\nexport const preloadQuery = createQueryPreloader(client);\n```\n\n### Using preloadQuery with Route Loaders\n\n> **Note**: This example applies to React Router in non-framework mode. For React Router framework mode, see [integration-react-router.md](./integration-react-router.md).\n\nUse the preload function with React Router's `loader` function to begin loading data during route transitions:\n\n```tsx\nimport { preloadQuery } from \"@/lib/apollo-client\";\nimport { GET_DOG } from \"./queries.generated\";\n\n// React Router loader function\nexport async function loader({ params }: { params: { id: string } }) {\n  return preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n}\n\n// Route component\nexport default function DogRoute() {\n  const queryRef = useLoaderData();\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <DogDetails queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction DogDetails({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.dog.name}</h1>\n      <p>Breed: {data.dog.breed}</p>\n    </div>\n  );\n}\n```\n\n### Preventing Route Transitions Until Query Loads\n\nUse `preloadQuery.toPromise(queryRef)` to prevent route transitions until the query finishes loading:\n\n```tsx\nexport async function loader({ params }: { params: { id: string } }) {\n  const queryRef = preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n\n  // Wait for the query to complete before transitioning\n  return preloadQuery.toPromise(queryRef);\n}\n```\n\nWhen `preloadQuery.toPromise()` is used, the route transition waits for the query to complete, and the data renders immediately without showing a loading fallback.\n\n> **Note**: `preloadQuery.toPromise()` resolves with the `queryRef` itself (not the data) to encourage using `useReadQuery` for cache updates. If you need raw query data in your loader, use `client.query()` directly.\n\n### With Next.js Server Components\n\n> **Note**: For Next.js App Router, use the `PreloadQuery` component from `@apollo/client-integration-nextjs` instead. See [integration-nextjs.md](./integration-nextjs.md) for details.\n\n## useQueryRefHandlers\n\nThe `useQueryRefHandlers` hook provides access to `refetch` and `fetchMore` functions for queries initiated with `preloadQuery`, `useBackgroundQuery`, or `useLoadableQuery`. This is useful when you need to refetch or paginate data in components where the `queryRef` is passed through.\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n### Basic Usage\n\n```tsx\nimport { useQueryRefHandlers } from \"@apollo/client/react\";\n\nfunction Breeds({ queryRef }: { queryRef: QueryRef<BreedsData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            refetch();\n          });\n        }}\n      >\n        {isPending ? \"Refetching...\" : \"Refetch breeds\"}\n      </button>\n      <ul>\n        {data.breeds.map((breed) => (\n          <li key={breed.id}>{breed.name}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### With Pagination\n\nUse `fetchMore` to implement pagination:\n\n```tsx\nfunction Posts({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { fetchMore } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            fetchMore({\n              variables: {\n                offset: data.posts.length,\n              },\n            });\n          });\n        }}\n      >\n        {isPending ? \"Loading...\" : \"Load more\"}\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Preloaded queries**: Access refetch/fetchMore for queries initiated with `preloadQuery`\n- **Background queries**: Use in child components receiving `queryRef` from `useBackgroundQuery`\n- **Loadable queries**: Refetch or paginate queries initiated with `useLoadableQuery`\n- **React transitions**: Integrate with transitions to avoid showing loading fallbacks during refetches\n\n## Distinguishing Queries with queryKey\n\nApollo Client uses the combination of `query` and `variables` to uniquely identify each query. When multiple components use the same `query` and `variables`, they share the same identity and suspend at the same time, regardless of which component initiates the request.\n\nUse the `queryKey` option to ensure each hook has a unique identity:\n\n```tsx\nfunction UserProfile() {\n  // First query with unique key\n  const { data: userData } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-profile\"],\n  });\n\n  // Second query with same query and variables but different key\n  const { data: userPreview } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-preview\"],\n  });\n\n  return (\n    <div>\n      <UserCard user={userData.user} />\n      <UserSidebar user={userPreview.user} />\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Multiple instances**: When rendering multiple components that use the same query and variables\n- **Preventing shared suspension**: When you want independent control over when each query suspends\n- **Separate cache entries**: When you need to maintain separate cache states for the same query\n\n> **Note**: Each item in the `queryKey` array must be a stable identifier to prevent infinite fetches.\n\n## Suspense Boundaries and Error Handling\n\n### Suspense Boundaries\n\nWrap components that use Suspense hooks with `<Suspense>` boundaries to handle loading states. Place boundaries strategically to control the granularity of loading indicators.\n\n```tsx\nfunction App() {\n  return (\n    <>\n      {/* Top-level loading for entire page */}\n      <Suspense fallback={<PageSpinner />}>\n        <Header />\n        <Content />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Content() {\n  return (\n    <>\n      <MainSection />\n      {/* Granular loading for sidebar */}\n      <Suspense fallback={<SidebarSkeleton />}>\n        <Sidebar />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Error Boundaries\n\nSuspense hooks throw errors to React error boundaries instead of returning them. Use error boundaries to handle GraphQL errors.\n\n```tsx\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nfunction App() {\n  return (\n    <ErrorBoundary\n      fallback={({ error }) => (\n        <div>\n          <h2>Something went wrong</h2>\n          <p>{error.message}</p>\n        </div>\n      )}\n    >\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id=\"3\" />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n```\n\n### Custom Error Policies\n\nUse `errorPolicy` to control how errors are handled:\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data, error } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    errorPolicy: \"all\", // Return both data and errors\n  });\n\n  return (\n    <>\n      <div>Name: {data?.dog?.name ?? \"Unknown\"}</div>\n      {error && <div>Warning: {error.message}</div>}\n    </>\n  );\n}\n```\n\n## Transitions\n\nUse React transitions to avoid showing loading UI when updating state. Transitions keep the previous UI visible while new data is fetching.\n\n### Using startTransition\n\n```tsx\nimport { useState, Suspense, startTransition } from \"react\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => {\n          // Wrap state update in startTransition\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Using useTransition\n\nUse `useTransition` to get an `isPending` flag for visual feedback during transitions.\n\n```tsx\nimport { useState, Suspense, useTransition } from \"react\";\n\nfunction DogSelector() {\n  const [isPending, startTransition] = useTransition();\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        style={{ opacity: isPending ? 0.5 : 1 }}\n        value={selectedDog}\n        onChange={(e) => {\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n## Avoiding Request Waterfalls\n\nRequest waterfalls occur when a child component waits for the parent to finish rendering before it can start fetching its own data. Use `useBackgroundQuery` to start fetching child data earlier in the component tree.\n\n> **Note**: When one query depends on the result of another query (e.g., the child query needs an ID from the parent query), the waterfall is unavoidable. The best solution is to restructure your schema to fetch all needed data in a single nested query.\n\n### Example: Independent Queries\n\nWhen queries don't depend on each other, use `useBackgroundQuery` to start them in parallel:\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: String!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nconst GET_POSTS = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n    }\n  }\n`;\n\nfunction Parent() {\n  // Both queries start immediately - no waterfall\n  const [userRef] = useBackgroundQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const [postsRef] = useBackgroundQuery(GET_POSTS);\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserProfile queryRef={userRef} />\n      <PostsList queryRef={postsRef} />\n    </Suspense>\n  );\n}\n\nfunction UserProfile({ queryRef }: { queryRef: QueryRef<UserData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>User: {data.user.name}</div>;\n}\n\nfunction PostsList({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <ul>\n      {data.posts.map((post) => (\n        <li key={post.id}>{post.title}</li>\n      ))}\n    </ul>\n  );\n}\n```\n\n## Fetch Policies\n\nSuspense hooks support most of the same fetch policies as `useQuery`, controlling how the query interacts with the cache. Note that `cache-only` and `standby` are not supported by Suspense hooks.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n\n### Usage Examples\n\n```tsx\n// Always fetch fresh data\nconst { data } = useSuspenseQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Prefer cached data\nconst { data } = useSuspenseQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data } = useSuspenseQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n```\n\n## Streaming SSR or React Server Components\n\nApollo Client integrates with modern React frameworks that support Streaming SSR and React Server Components. For detailed setup instructions specific to your framework, see:\n\n- **Next.js App Router**: [integration-nextjs.md](./integration-nextjs.md) - Includes React Server Components, PreloadQuery component, and streaming SSR\n- **React Router**: [integration-react-router.md](./integration-react-router.md) - Framework mode with SSR support\n- **TanStack Start**: [integration-tanstack-start.md](./integration-tanstack-start.md) - Full-stack React framework with SSR\n\nThese guides cover:\n\n- Framework-specific client setup and configuration\n- Preloading queries for optimal performance\n- Streaming SSR with `useBackgroundQuery` and Suspense\n- Error handling in server-rendered environments\n\n## Conditional Queries\n\n### Using skipToken\n\nUse `skipToken` to conditionally skip queries without TypeScript issues. When `skipToken` is used, the component won't suspend and `data` will be `undefined`.\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data, dataState } = useSuspenseQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  if (dataState !== \"complete\") {\n    return <p>Select a user</p>;\n  }\n\n  return <Profile user={data.user} />;\n}\n```\n\n### Conditional Rendering\n\nAlternatively, use conditional rendering to control when Suspense hooks are called. This provides better type safety and clearer component logic.\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  if (!userId) {\n    return <p>Select a user</p>;\n  }\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserDetails userId={userId} />\n    </Suspense>\n  );\n}\n\nfunction UserDetails({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <Profile user={data.user} />;\n}\n```\n\n> **Note**: Using conditional rendering with `skipToken` provides better type safety and avoids issues with required variables. The `skip` option is deprecated in favor of `skipToken`.\n","docs/agent-skills/apollo-client/references/troubleshooting.md":"# Troubleshooting Reference\n\n## Table of Contents\n\n- [Setup Issues](#setup-issues)\n- [Cache Issues](#cache-issues)\n- [TypeScript Issues](#typescript-issues)\n- [Performance Issues](#performance-issues)\n- [DevTools Usage](#devtools-usage)\n- [Common Error Messages](#common-error-messages)\n\n## Setup Issues\n\n### Provider Not Found\n\n**Error:** `Could not find \"client\" in the context or passed in as an option`\n\n**Cause:** Component is not wrapped with `ApolloProvider`.\n\n**Solution:**\n\n```tsx\n// Ensure ApolloProvider wraps your app\nimport { ApolloProvider } from \"@apollo/client\";\n\nfunction App() {\n  return (\n    <ApolloProvider client={client}>\n      <YourApp />\n    </ApolloProvider>\n  );\n}\n```\n\n### Multiple Apollo Clients\n\n**Problem:** Unintended cache isolation or conflicting states.\n\n**Solution:** Use a single client instance or explicitly manage multiple clients:\n\n```tsx\n// Single client (recommended)\nconst client = new ApolloClient({\n  /* ... */\n});\n\nexport function App() {\n  return (\n    <ApolloProvider client={client}>\n      <Router />\n    </ApolloProvider>\n  );\n}\n\n// Multiple clients (rare use case)\nconst publicClient = new ApolloClient({\n  uri: \"/public/graphql\",\n  cache: new InMemoryCache(),\n});\nconst adminClient = new ApolloClient({\n  uri: \"/admin/graphql\",\n  cache: new InMemoryCache(),\n});\n\nfunction AdminSection() {\n  return (\n    <ApolloProvider client={adminClient}>\n      <AdminDashboard />\n    </ApolloProvider>\n  );\n}\n```\n\n### Client Created in Component\n\n**Problem:** New client on every render causes cache loss.\n\n**Solution:** Create client outside component or use a ref pattern:\n\n```tsx\n// Bad - new client on every render\nfunction App() {\n  const client = new ApolloClient({\n    /* ... */\n  }); // Don't do this!\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Module-level client definition\n// Okay if there is a 100% guarantee this application will never use SSR\nconst client = new ApolloClient({\n  /* ... */\n});\nfunction App() {\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Good - store Apollo Client in a ref that is initialized once\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    storeRef.current = makeApolloClient();\n  }\n  return storeRef.current;\n}\n\n// Better - singleton global in non-SSR environments to survive unmounts\nconst singleton = Symbol.for(\"ApolloClientSingleton\");\ndeclare global {\n  interface Window {\n    [singleton]?: ApolloClient;\n  }\n}\n\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    if (typeof window === \"undefined\") {\n      storeRef.current = makeApolloClient();\n    } else {\n      window[singleton] ??= makeApolloClient();\n      storeRef.current = window[singleton];\n    }\n  }\n  return storeRef.current;\n}\n// Note: this second option might need manual removal between tests\n```\n\n## Cache Issues\n\n### Stale Data Not Updating\n\n**Problem:** UI doesn't reflect mutations or other updates.\n\n**Solution 1:** Verify cache key identification:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Ensure proper identification\n    Product: {\n      keyFields: [\"id\"], // or ['sku'] if no id field\n    },\n  },\n});\n```\n\n**Solution 2:** Update cache after mutations:\n\n```tsx\nconst [deleteProduct] = useMutation(DELETE_PRODUCT, {\n  update: (cache, { data }) => {\n    cache.evict({ id: cache.identify(data.deleteProduct) });\n    cache.gc();\n  },\n});\n```\n\n**Solution 3:** Use appropriate fetch policy:\n\n```tsx\nconst { data } = useQuery(GET_PRODUCTS, {\n  fetchPolicy: \"cache-and-network\", // Always fetch fresh data\n});\n```\n\n### Missing Cache Updates After Mutation\n\n**Problem:** New items don't appear in lists after creation.\n\n**Solution:** Manually update the cache:\n\n```tsx\nconst [createProduct] = useMutation(CREATE_PRODUCT, {\n  update: (cache, { data }) => {\n    const existing = cache.readQuery<{ products: Product[] }>({\n      query: GET_PRODUCTS,\n    });\n\n    cache.writeQuery({\n      query: GET_PRODUCTS,\n      data: {\n        products: [...(existing?.products ?? []), data.createProduct],\n      },\n    });\n  },\n});\n```\n\n### Pagination Cache Issues\n\n**Problem:** Paginated data not merging correctly.\n\n**Solution:** Configure proper type policies:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        products: {\n          keyArgs: [\"category\"], // Only category creates new cache entries\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Cache Normalization Problems\n\n**Problem:** Objects with same ID showing different data in different queries.\n\n**Debug:** Check cache contents:\n\n```typescript\n// In DevTools console or component\nconsole.log(client.cache.extract());\n```\n\n**Solution:** Ensure consistent `__typename` and `id` fields:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Always include id\n    name\n  }\n}\n```\n\n## TypeScript Issues\n\n### Type Generation Setup\n\n**Problem:** No type safety for GraphQL operations.\n\n**Solution:** Set up GraphQL Code Generator with the [recommended starter configuration](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration), as described in the [Skill](../SKILL.md).\n\n### Using Generated Types\n\n```tsx\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUsersDocument, GetUsersQuery } from \"./generated/graphql\";\n\nfunction UserList() {\n  // Fully typed without manual type annotations\n  const { data, loading, error } = useQuery(GetUsersDocument);\n\n  // data.users is automatically typed as GetUsersQuery['users']\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Performance Issues\n\n### Over-Fetching\n\n**Problem:** Fetching more data than needed.\n\n**Solution:** Select only required fields:\n\n```graphql\n# Bad - fetching everything\nquery GetUsers {\n  users {\n    id\n    name\n    email\n    profile { ... }\n    posts { ... }\n    friends { ... }\n  }\n}\n\n# Good - fetch what's needed\nquery GetUserNames {\n  users {\n    id\n    name\n  }\n}\n```\n\n### N+1 Queries\n\n**Problem:** Multiple network requests for related data.\n\n**Solution:** Structure queries to batch requests. Best practice: use query colocation and compose queries from fragments defined on child components.\n\n```graphql\n# Bad - separate queries\nquery GetUser($id: ID!) {\n  user(id: $id) {\n    id\n    name\n  }\n}\nquery GetUserPosts($userId: ID!) {\n  posts(userId: $userId) {\n    id\n    title\n  }\n}\n\n# Good - single query\nquery GetUserWithPosts($id: ID!) {\n  user(id: $id) {\n    id\n    name\n    posts {\n      id\n      title\n    }\n  }\n}\n```\n\n### Unnecessary Re-Renders\n\n**Problem:** Components re-render when unrelated cache data changes.\n\n**Solution:** Use `useFragment` and data masking for selective field reading. If that is not possible, `useQuery` with `@nonreactive` directives might be an alternative.\n\n```tsx\n// Prefer useFragment with data masking\nconst { data } = useFragment({\n  fragment: USER_FRAGMENT,\n  from: { __typename: \"User\", id },\n});\n\n// Alternative: use @nonreactive directive\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      # This field won't trigger re-renders when it changes\n      metadata @nonreactive {\n        lastSeen\n        preferences\n      }\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_USER, {\n  variables: { id },\n});\n```\n\n### Cache Misses\n\n**Debug:** Use Apollo DevTools to inspect cache.\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  // DevTools are enabled by default in development\n  // Only configure this when you need to enable them in production\n  devtools: {\n    enabled: true,\n  },\n});\n```\n\n## DevTools Usage\n\n### Installing Apollo DevTools\n\nInstall the browser extension:\n\n- [Chrome](https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)\n- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/)\n\n### Enabling DevTools\n\nDevTools are enabled by default in development. Only configure this setting if you need to enable them in production:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  devtools: {\n    enabled: true, // Set to true to enable in production\n  },\n});\n```\n\n### DevTools Features\n\n1. **Cache Inspector**: View normalized cache contents\n2. **Queries**: See active queries and their states\n3. **Mutations**: Track mutation history\n4. **Explorer**: Build and test queries against your schema\n5. **Memoization Limits**: Monitor and track cache memoization\n6. **Cache Writes**: Track all writes to the cache\n\n### Debugging Cache\n\n```typescript\n// Log cache contents\nconsole.log(JSON.stringify(client.cache.extract(), null, 2));\n\n// Check specific object using cache.identify\nconsole.log(\n  client.cache.readFragment({\n    id: cache.identify({ __typename: \"User\", id: 1 }),\n    fragment: gql`\n      fragment _ on User {\n        id\n        name\n        email\n      }\n    `,\n  })\n);\n```\n\n## Common Error Messages\n\n### \"Missing field 'X' in {...}\"\n\n**Cause:** Query doesn't include required field for cache normalization.\n\n**Solution:** Include `id` and `__typename`:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Required for caching\n    __typename # Usually added automatically\n    name\n  }\n}\n```\n\n**Additional advice**: Read the full error message thoroughly.\n\n### \"Store reset while query was in flight\"\n\n**Cause:** `client.resetStore()` called during active queries.\n\n**Solution:** Wait for queries to complete or use `clearStore()`:\n\n```typescript\n// Option 1: Clear without refetching\nawait client.clearStore();\n\n// Option 2: Reset and refetch active queries\nawait client.resetStore();\n```\n\n### \"Invariant Violation: X\"\n\n**Cause:** Various configuration or usage errors.\n\n**Common fixes:**\n\n- Ensure `ApolloProvider` wraps the component tree\n- Check that `gql` tagged templates are valid GraphQL\n- Verify cache configuration matches your schema\n\n### \"Cannot read property 'X' of undefined\"\n\n**Cause:** Accessing data before query completes.\n\n**Solution:** Check `dataState` for proper type narrowing:\n\n```tsx\nconst { data, dataState } = useQuery(GET_USER);\n\n// dataState can be \"complete\", \"partial\", \"streaming\", or \"empty\"\n// It describes the completeness of the data, not a loading state\nif (dataState === \"empty\") return <Spinner />;\n\n// Now data is guaranteed to exist\nreturn <div>{data.user.name}</div>;\n\n// Or use optional chaining\nreturn <div>{data?.user?.name}</div>;\n```\n","docs/agent-skills/apollo-client/references/typescript-codegen.md":"# TypeScript Code Generation\n\nThis guide covers setting up GraphQL Code Generator for type-safe Apollo Client usage with TypeScript.\n\n## Installation\n\n```bash\nnpm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node\n```\n\n## Configuration\n\nCreate a `codegen.ts` file in your project root:\n\n```typescript\n// codegen.ts\nimport { CodegenConfig } from \"@graphql-codegen/cli\";\n\nconst config: CodegenConfig = {\n  overwrite: true,\n  schema: \"<URL_OF_YOUR_GRAPHQL_API>\",\n  // This assumes that all your source files are in a top-level `src/` directory - you might need to adjust this to your file structure\n  documents: [\"src/**/*.{ts,tsx}\"],\n  // Don't exit with non-zero status when there are no documents\n  ignoreNoDocuments: true,\n  generates: {\n    // Use a path that works the best for the structure of your application\n    \"./src/types/__generated__/graphql.ts\": {\n      plugins: [\"typescript\", \"typescript-operations\", \"typed-document-node\"],\n      config: {\n        avoidOptionals: {\n          // Use `null` for nullable fields instead of optionals\n          field: true,\n          // Allow nullable input fields to remain unspecified\n          inputValue: false,\n        },\n        // Use `unknown` instead of `any` for unconfigured scalars\n        defaultScalarType: \"unknown\",\n        // Apollo Client always includes `__typename` fields\n        nonOptionalTypename: true,\n        // Apollo Client doesn't add the `__typename` field to root types so\n        // don't generate a type for the `__typename` for root operation types.\n        skipTypeNameForRoot: true,\n      },\n    },\n  },\n};\n\nexport default config;\n```\n\n## Enable Data Masking\n\nTo enable data masking with GraphQL Code Generator, create a type declaration file to inform Apollo Client about the generated types:\n\n```typescript\n// apollo-client.d.ts\nimport { GraphQLCodegenDataMasking } from \"@apollo/client/masking\";\n\ndeclare module \"@apollo/client\" {\n  export interface TypeOverrides\n    extends GraphQLCodegenDataMasking.TypeOverrides {}\n}\n```\n\n## Running Code Generation\n\nAdd a script to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  }\n}\n```\n\nRun code generation:\n\n```bash\nnpm run codegen\n```\n\n## Usage with Apollo Client\n\nThe typed-document-node plugin generates `TypedDocumentNode` types that Apollo Client hooks automatically infer.\n\n### Defining Operations\n\nDefine your operations inline with the `if (false)` pattern. This allows GraphQL Code Generator to detect and extract operations without executing the code at runtime (bundlers omit this dead code during minification):\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\n// This query will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n```\n\n### Using Generated Types\n\nAfter running `npm run codegen`, import the generated `TypedDocumentNode`:\n\n```typescript\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  // ... other logic ...\n\n  return <div>{data?.user.name}</div>;\n}\n```\n\n## Important Notes\n\n- The typed-document-node plugin might have a bundle size tradeoff but can prevent inconsistencies and is best suited for usage with LLMs, so it is recommended for most applications.\n- See the [GraphQL Code Generator documentation](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration) for other recommended configuration patterns if required.\n- Apollo Client hooks automatically infer types from `TypedDocumentNode` - never use manual generics like `useQuery<QueryType, VariablesType>()`.\n",".cursorrules (react-graphql-apollo-client-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for GraphQL development with Apollo Client integration.\"\nglobs: **/*\nalwaysApply: false\n---\n// React + GraphQL (Apollo Client) .cursorrules\n\n// Prefer functional components with hooks\n\nconst preferFunctionalComponents = true;\n\n// GraphQL and Apollo Client best practices\n\nconst graphqlBestPractices = [\n  \"Use Apollo Client for state management and data fetching\",\n  \"Implement query components for data fetching\",\n  \"Utilize mutations for data modifications\",\n  \"Use fragments for reusable query parts\",\n  \"Implement proper error handling and loading states\",\n];\n\n// Folder structure\n\nconst folderStructure = `\nsrc/\n  components/\n  graphql/\n    queries/\n    mutations/\n    fragments/\n  hooks/\n  pages/\n  utils/\n`;\n\n// Additional instructions\n\nconst additionalInstructions = `\n1. Use Apollo Provider at the root of your app\n2. Implement custom hooks for Apollo operations\n3. Use TypeScript for type safety with GraphQL operations\n4. Utilize Apollo Client's caching capabilities\n5. Implement proper error boundaries for GraphQL errors\n6. Use Apollo Client DevTools for debugging\n7. Follow naming conventions for queries, mutations, and fragments\n`;\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Apollo Client Overview\n\nApollo Client is a comprehensive caching GraphQL client for TypeScript/JavaScript. The project is maintained at version 4.x.x and uses a monorepo structure with npm workspaces.\n\n## Common Development Commands\n\n### Build Commands\n\n- `npm run build` - Build the entire project\n- `npm run build -w codegen` - Build specific workspace (e.g., codegen)\n\n### Testing Commands\n\n- `npm test` - Run all tests with Jest\n- `npm run test:type-benches` - Run type benchmarks\n- `npm run test:memory` - Run memory tests\n\n### Code Quality Commands\n\n- `npm run typecheck` - Run TypeScript type checking\n- `npm run lint` - Run ESLint with extended rules\n- `npm run format` - Format code with Prettier\n- `npm run check:format` - Check if code is formatted correctly\n- `npm run knip` - Check for unused files/exports\n- `npm run madge` - Check for circular dependencies\n\n### Documentation & API\n\n- `npm run extract-api` - Generate API documentation\n- `npm run typedoc` - Generate TypeDoc documentation\n- `npm run docmodel` - Generate API documentation model\n\n### Release & Publishing\n\n- `npx changeset` - Create a changeset for your changes\n\n## High-Level Architecture\n\n### Core Modules\n\n1. **Core (`src/core/`)** - Main Apollo Client implementation\n\n   - `ApolloClient.ts` - Main client class\n   - `ObservableQuery.ts` - Observable query implementation\n   - `QueryManager.ts` - Query lifecycle management\n\n2. **Cache (`src/cache/`)** - Caching layer\n\n   - `inmemory/` - InMemoryCache implementation (default cache)\n   - `core/` - Base cache abstractions\n\n3. **Link (`src/link/`)** - Network layer abstraction\n\n   - `http/` - HTTP link for GraphQL over HTTP\n   - `batch/` - Batch link for combining queries\n   - `error/` - Error handling link\n   - `schema/` - Schema link for local execution\n   - `ws/` - WebSocket link for subscriptions\n\n4. **React Integration (`src/react/`)** - React hooks and components\n\n   - `hooks/` - React hooks (useQuery, useMutation, etc.)\n   - `ssr/` - Server-side rendering support\n   - `context/` - React context providers\n\n5. **Utilities (`src/utilities/`)** - Shared utilities\n\n   - `graphql/` - GraphQL document utilities\n   - `internal/` - Internal utilities\n   - `invariant/` - Error handling utilities\n\n6. **Testing (`src/testing/`)** - Testing utilities\n\n   - `core/` - Core testing utilities\n   - `react/` - React testing utilities (MockedProvider)\n\n7. **Masking (`src/masking/`)** - Data masking for fragment colocation\n\n8. **Local State (`src/local-state/`)** - Local state management\n\n### TypeScript Configuration\n\n- Uses TypeScript 5.7.3 with strict mode enabled\n- Module resolution: NodeNext\n- Target: ESNext\n- Separate tsconfig files for different purposes:\n  - `tsconfig.json` - Main configuration\n  - `tsconfig.build.json` - Build configuration\n  - `tsconfig.tests.json` - Test configuration\n\n### Testing Infrastructure\n\n- Jest with ts-jest for testing\n- Custom test environment extending jsdom\n- React Testing Library for React component tests\n- Multiple React version testing (17, 18, 19)\n- Coverage reporting with lcov\n\n### Build System\n\n- Uses custom build script (`config/build.ts`)\n- API Extractor for API documentation\n- Size limit checking for bundle size\n- Source map verification\n- Publint for package publishing validation\n\n### Code Organization Patterns\n\n1. **Exports** - All public exports go through index files\n2. **Internal APIs** - Use `@apollo/client/*/internal` paths\n3. **Environment-specific builds** - Production/development variants\n4. **Type safety** - Heavy use of TypeScript generics and type inference\n5. **Reactive programming** - Uses RxJS for observables\n6. **Immutability** - Emphasizes immutable data structures\n\n### Contributing Guidelines\n\n- All changes require a changeset (`npx changeset`)\n- PRs should include tests for bug fixes\n- Follow existing code style (enforced by ESLint/Prettier)\n- Large features need design discussion first\n- Code review required from core contributors\n\n### Important Notes\n\n- This is a monorepo with workspaces: main package, codegen, and codemods\n- The project is currently at version 4.x.x (release candidate)\n- Supports multiple peer dependencies (React 17/18/19, GraphQL 16)\n- Uses patch-package for dependency patches\n- Has extensive CI checks that must pass before merging\n\n## Additional Instructions\n\n@.claude/documentation.md\n@.claude/hooks.md\n","isInternal":false,"tokens":1092,"sizeBytes":4486},{"name":"caching.md","path":"docs/agent-skills/apollo-client/references/caching.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/caching.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Caching Reference\n\n## Table of Contents\n\n- [InMemoryCache Setup](#inmemorycache-setup)\n- [Cache Normalization](#cache-normalization)\n- [Type Policies](#type-policies)\n- [Field Policies](#field-policies)\n- [Pagination](#pagination)\n- [Cache Manipulation](#cache-manipulation)\n- [Garbage Collection](#garbage-collection)\n\n## InMemoryCache Setup\n\n### Basic Configuration\n\n```typescript\nimport { InMemoryCache } from \"@apollo/client\";\n\nconst cache = new InMemoryCache({\n  // Custom type policies\n  typePolicies: {\n    Query: {\n      fields: {\n        // Query-level field policies\n      },\n    },\n    User: {\n      keyFields: [\"id\"],\n      fields: {\n        // User-level field policies\n      },\n    },\n  },\n\n  // Custom type name handling (rare)\n  possibleTypes: {\n    Character: [\"Human\", \"Droid\"],\n    Node: [\"User\", \"Post\", \"Comment\"],\n  },\n});\n```\n\n### Constructor Options\n\n```typescript\nnew InMemoryCache({\n  // Define how types are identified in cache\n  typePolicies: {\n    /* ... */\n  },\n\n  // Interface/union type mappings between supertypes and their subtypes\n  possibleTypes: {\n    /* ... */\n  },\n\n  // Custom function to generate cache IDs (rare)\n  dataIdFromObject: (object) => {\n    if (object.__typename === \"Book\") {\n      return `Book:${object.isbn}`;\n    }\n    return defaultDataIdFromObject(object);\n  },\n});\n```\n\n## Cache Normalization\n\nApollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.\n\n### How Normalization Works\n\n```graphql\n# Query\nquery GetPost {\n  post(id: \"1\") {\n    id\n    title\n    author {\n      id\n      name\n    }\n  }\n}\n```\n\n```typescript\n// Normalized cache structure\n{\n  'Post:1': {\n    __typename: 'Post',\n    id: '1',\n    title: 'Hello World',\n    author: { __ref: 'User:42' }\n  },\n  'User:42': {\n    __typename: 'User',\n    id: '42',\n    name: 'John'\n  },\n  ROOT_QUERY: {\n    'post({\"id\":\"1\"})': { __ref: 'Post:1' }\n  }\n}\n```\n\n### Benefits of Normalization\n\n1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update\n2. **Deduplication**: Same objects aren't stored multiple times\n3. **Efficient updates**: Only changed objects trigger re-renders\n\n## Type Policies\n\n### keyFields\n\nCustomize how objects are identified in the cache.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Use ISBN instead of id for books\n    Book: {\n      keyFields: [\"isbn\"],\n    },\n\n    // Composite key\n    UserSession: {\n      keyFields: [\"userId\", \"deviceId\"],\n    },\n\n    // Nested key\n    Review: {\n      keyFields: [\"book\", [\"isbn\"], \"reviewer\", [\"id\"]],\n    },\n\n    // No key fields (singleton, only one object in cache per type)\n    AppSettings: {\n      keyFields: [],\n    },\n\n    // Disable normalization (objects of this type will be stored with their\n    // parent entity. The same object might end up multiple times in the cache\n    // and run out of sync. Use with caution, only if this object really relates\n    // to a property of their parent entity and cannot exist on its own.)\n    Address: {\n      keyFields: false,\n    },\n  },\n});\n```\n\n### merge Functions\n\nControl how incoming data merges with existing data.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Deep merge profile object\n        profile: {\n          merge: true, // Shorthand for deep merge\n        },\n\n        // Custom merge logic\n        notifications: {\n          merge(existing = [], incoming, { mergeObjects }) {\n            // Prepend new notifications\n            return [...incoming, ...existing];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## Field Policies\n\n### read Function\n\nTransform cached data when reading.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Computed field\n        fullName: {\n          read(_, { readField }) {\n            const firstName = readField(\"firstName\");\n            const lastName = readField(\"lastName\");\n            return `${firstName} ${lastName}`;\n          },\n        },\n\n        // Transform existing field\n        birthDate: {\n          read(existing) {\n            return existing ? new Date(existing) : null;\n          },\n        },\n\n        // Default value\n        role: {\n          read(existing = \"USER\") {\n            return existing;\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### merge Function\n\nControl how incoming data is stored.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Accumulate items instead of replacing\n        friends: {\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n\n        // Merge objects deeply\n        settings: {\n          merge(existing, incoming, { mergeObjects }) {\n            return mergeObjects(existing, incoming);\n          },\n        },\n      },\n    },\n\n    Query: {\n      fields: {\n        // Merge paginated results\n        posts: {\n          keyArgs: [\"category\"], // Only category affects cache key\n          merge(existing = { items: [] }, incoming) {\n            return {\n              ...incoming,\n              items: [...existing.items, ...incoming.items],\n            };\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### keyArgs\n\nControl which arguments affect cache storage.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        // Different cache entry per userId only\n        // (limit, offset don't create new entries)\n        userPosts: {\n          keyArgs: [\"userId\"],\n        },\n\n        // No arguments affect cache key\n        // (useful for pagination)\n        feed: {\n          keyArgs: false,\n        },\n\n        // Nested argument\n        search: {\n          keyArgs: [\"filter\", [\"category\", \"status\"]],\n        },\n      },\n    },\n  },\n});\n```\n\n## Pagination\n\n### Offset-Based Pagination\n\n```typescript\nimport { offsetLimitPagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: offsetLimitPagination(),\n\n        // With key arguments\n        userPosts: offsetLimitPagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Cursor-Based Pagination (Relay Style)\n\n```typescript\nimport { relayStylePagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: relayStylePagination(),\n\n        // With key arguments\n        userPosts: relayStylePagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Custom Pagination\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        feed: {\n          keyArgs: false,\n\n          merge(existing, incoming, { args }) {\n            const merged = existing ? existing.slice(0) : [];\n            const offset = args?.offset ?? 0;\n\n            for (let i = 0; i < incoming.length; i++) {\n              merged[offset + i] = incoming[i];\n            }\n\n            return merged;\n          },\n\n          read(existing, { args }) {\n            const offset = args?.offset ?? 0;\n            const limit = args?.limit ?? existing?.length ?? 0;\n            return existing?.slice(offset, offset + limit);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### fetchMore for Pagination\n\n```tsx\nfunction PostList() {\n  const { data, fetchMore, loading } = useQuery(GET_POSTS, {\n    variables: { offset: 0, limit: 10 },\n  });\n\n  const loadMore = () => {\n    fetchMore({\n      variables: {\n        offset: data.posts.length,\n      },\n      // With proper type policies, no updateQuery needed\n    });\n  };\n\n  return (\n    <div>\n      {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}\n      <button onClick={loadMore} disabled={loading}>\n        Load More\n      </button>\n    </div>\n  );\n}\n```\n\n## Cache Manipulation\n\n### cache.readQuery\n\n```typescript\n// Read data from cache\nconst data = cache.readQuery({\n  query: GET_TODOS,\n});\n\n// With variables\nconst userData = cache.readQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n});\n```\n\n### cache.writeQuery\n\n```typescript\n// Write data to cache\ncache.writeQuery({\n  query: GET_TODOS,\n  data: {\n    todos: [\n      { __typename: \"Todo\", id: \"1\", text: \"Buy milk\", completed: false },\n    ],\n  },\n});\n\n// With variables\ncache.writeQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n  data: {\n    user: { __typename: \"User\", id: \"1\", name: \"John\" },\n  },\n});\n```\n\n### cache.readFragment / cache.writeFragment\n\n```typescript\n// Read a specific object - use cache.identify for safety\nconst user = cache.readFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\nconst user = cache.readFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Update a specific object\ncache.writeFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\ncache.writeFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n```\n\n### cache.modify\n\n```typescript\n// Modify fields directly\ncache.modify({\n  id: cache.identify(user),\n  fields: {\n    // Set new value\n    name: () => \"New Name\",\n\n    // Transform existing value\n    postCount: (existing) => existing + 1,\n\n    // Delete field\n    temporaryField: (_, { DELETE }) => DELETE,\n\n    // Add to array\n    friends: (existing, { toReference }) => [\n      ...existing,\n      toReference({ __typename: \"User\", id: \"2\" }),\n    ],\n  },\n});\n```\n\n### cache.evict\n\n```typescript\n// Remove object from cache\ncache.evict({ id: \"User:1\" });\n\n// Remove specific field\ncache.evict({ id: \"User:1\", fieldName: \"friends\" });\n\n// Remove with broadcast (trigger re-renders)\ncache.evict({ id: \"User:1\", broadcast: true });\n```\n\n## Garbage Collection\n\n### Manual Garbage Collection\n\n```typescript\n// After evicting objects, clean up dangling references\ncache.evict({ id: \"User:1\" });\ncache.gc();\n```\n\n### Retaining Objects\n\n```typescript\n// Prevent objects from being garbage collected\nconst release = cache.retain(\"User:1\");\n\n// Later, allow GC\nrelease();\ncache.gc();\n```\n\n### Inspecting Cache\n\n```typescript\n// Get all cached data\nconst cacheContents = cache.extract();\n\n// Restore cache state\ncache.restore(previousCacheContents);\n\n// Get identified object cache key\nconst userId = cache.identify({ __typename: \"User\", id: \"1\" });\n// Returns: 'User:1'\n```\n","isInternal":false,"tokens":2653,"sizeBytes":10903},{"name":"error-handling.md","path":"docs/agent-skills/apollo-client/references/error-handling.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/error-handling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Error Handling Reference (Apollo Client 4.x)\n\nNote that Apollo Client 4.x handles errors differently than Apollo Client 3.x.\nThis reference documents the updated error handling mechanisms, error types, and best practices for managing errors in your Apollo Client applications.\nFor older Apollo Client 3.x error handling documentation, see [Apollo Client 3.x Error Handling](https://www.apollographql.com/docs/react/v3/data/error-handling).\n\n## Table of Contents\n\n- [Understanding Errors](#understanding-errors)\n- [Error Types](#error-types)\n- [Identifying Error Types](#identifying-error-types)\n- [GraphQL Error Policies](#graphql-error-policies)\n- [Error Links](#error-links)\n- [Retry Link](#retry-link)\n- [Error Boundaries](#error-boundaries)\n\n## Understanding Errors\n\nErrors in Apollo Client fall into two main categories: **GraphQL errors** and **network errors**. Each category has specific error classes that provide detailed information about what went wrong.\n\n### GraphQL Errors\n\nGraphQL errors are related to server-side execution of a GraphQL operation:\n\n- **Syntax errors** (e.g., malformed query)\n- **Validation errors** (e.g., query includes a non-existent schema field)\n- **Resolver errors** (e.g., error while populating a query field)\n\nIf a syntax or validation error occurs, the server doesn't execute the operation. If resolver errors occur, the server can still return partial data.\n\nExample server response with GraphQL error:\n\n```json\n{\n  \"errors\": [\n    {\n      \"message\": \"Cannot query field \\\"nonexistentField\\\" on type \\\"Query\\\".\",\n      \"locations\": [{ \"line\": 2, \"column\": 3 }],\n      \"extensions\": {\n        \"code\": \"GRAPHQL_VALIDATION_FAILED\"\n      }\n    }\n  ],\n  \"data\": null\n}\n```\n\nIn Apollo Client 4.x, GraphQL errors are represented by the [`CombinedGraphQLErrors`](https://apollographql.com/docs/react/api/errors/CombinedGraphQLErrors) error type.\n\n### Network Errors\n\nNetwork errors occur when attempting to communicate with your GraphQL server:\n\n- `4xx` or `5xx` HTTP response status codes\n- Network unavailability\n- JSON parsing failures\n- Custom errors from Apollo Link request handlers\n\nNetwork errors might be represented by special error types, but if an api such as the `fetch` API throws a native error (e.g., `TypeError`), Apollo Client will pass it through as-is.\nThrown values that don't fulfill the standard `ErrorLike` interface are wrapped in the [`UnconventionalError`](https://apollographql.com/docs/react/api/errors/UnconventionalError) class, which fulfills the `ErrorLike` interface. As such, you can expect any error returned by Apollo Client to fulfill the `ErrorLike` interface.\n\n```ts\nexport interface ErrorLike {\n  message: string;\n  name: string;\n  stack?: string;\n}\n```\n\n## Error Types\n\nApollo Client 4.x provides specific error classes for different error scenarios:\n\n### CombinedGraphQLErrors\n\nRepresents GraphQL errors returned by the server. Most common error type in applications.\n\n```tsx\nimport { CombinedGraphQLErrors } from \"@apollo/client/errors\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, error } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // no need to check for nullishness of error, CombinedGraphQLErrors.is handles that\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    return (\n      <div>\n        {error.graphQLErrors.map((err, i) => (\n          <p key={i}>GraphQL Error: {err.message}</p>\n        ))}\n      </div>\n    );\n  }\n\n  return data ? <Profile user={data.user} /> : null;\n}\n```\n\n### CombinedProtocolErrors\n\nRepresents fatal transport-level errors during multipart HTTP subscription execution.\n\n### ServerError\n\nOccurs when the server responds with a non-200 HTTP status code.\n\n```tsx\nimport { ServerError } from \"@apollo/client/errors\";\n\nif (ServerError.is(error)) {\n  console.error(\"Server error:\", error.statusCode, error.result);\n}\n```\n\n### ServerParseError\n\nOccurs when the server response cannot be parsed as valid JSON.\n\n```tsx\nimport { ServerParseError } from \"@apollo/client/errors\";\n\nif (ServerParseError.is(error)) {\n  console.error(\"Invalid JSON response:\", error.bodyText);\n}\n```\n\n### LocalStateError\n\nRepresents errors in local state configuration or execution.\n\n### UnconventionalError\n\nWraps non-standard errors (e.g., thrown symbols or objects) to ensure consistent error handling.\n\n## Identifying Error Types\n\nEvery Apollo Client error class provides a static `is` method that reliably determines whether an error is of that specific type. This is more robust than `instanceof` because it avoids false positives/negatives.\n\n```ts\nimport {\n  CombinedGraphQLErrors,\n  CombinedProtocolErrors,\n  LocalStateError,\n  ServerError,\n  ServerParseError,\n  UnconventionalError,\n  ErrorLike,\n} from \"@apollo/client/errors\";\n\n// Anything returned in the `error` field of Apollo Client hooks or methods is of type `ErrorLike` or `undefined`.\nfunction handleError(error?: ErrorLike) {\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    console.error(\"GraphQL errors:\", error.graphQLErrors);\n  } else if (CombinedProtocolErrors.is(error)) {\n    // Handle multipart subscription protocol errors\n  } else if (LocalStateError.is(error)) {\n    // Handle errors thrown by the LocalState class\n  } else if (ServerError.is(error)) {\n    // Handle server HTTP errors\n    console.error(\"Server error:\", error.statusCode);\n  } else if (ServerParseError.is(error)) {\n    // Handle JSON parse errors\n  } else if (UnconventionalError.is(error)) {\n    // Handle errors thrown by irregular types\n  } else if (error) {\n    // Handle other errors\n  }\n}\n```\n\n## GraphQL Error Policies\n\nIf a GraphQL operation produces errors, the server's response might still include partial data:\n\n```json\n{\n  \"data\": {\n    \"getInt\": 12,\n    \"getString\": null\n  },\n  \"errors\": [\n    {\n      \"message\": \"Failed to get string!\"\n    }\n  ]\n}\n```\n\nBy default, Apollo Client throws away partial data and populates the `error` field. You can use partial results by defining an **error policy**:\n\n| Policy   | Description                                                                                                                                                                  |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `none`   | (Default) If the response includes errors, they are returned in `error` and response `data` is set to `undefined` even if the server returns `data`.                         |\n| `ignore` | Errors are ignored (`error` is not populated), and any returned `data` is cached and rendered as if no errors occurred. `data` may be `undefined` if a network error occurs. |\n| `all`    | Both `data` and `error` are populated and any returned `data` is cached, enabling you to render both partial results and error information.                                  |\n\n### Setting an Error Policy\n\n```tsx\nconst MY_QUERY = gql`\n  query WillFail {\n    badField # This field's resolver produces an error\n    goodField # This field is populated successfully\n  }\n`;\n\nfunction ShowingSomeErrors() {\n  const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: \"all\" });\n\n  if (loading) return <span>loading...</span>;\n\n  return (\n    <div>\n      <h2>Good: {data?.goodField}</h2>\n      {error && <pre>Bad: {error.message}</pre>}\n    </div>\n  );\n}\n```\n\n### Avoid setting a Global Error Policy\n\nWhile it is possible to set a global error policy using `defaultOptions`, in practice this is discouraged as it can lead to unexpected behavior and type safety issues. The return types of the TypeScript hooks may change depending on the `errorPolicy` passed into the hook, and this can conceptually not take global `defaultOptions` error policies into account. As such, it is best to set the `errorPolicy` per operation as needed.\n\n## Error Links\n\nThe `ErrorLink` can be used to e.g. log error globally or perform specific side effects based on errors happening.\n\nAn `ErrorLink` can't be used to swallow errors fully, but it can be used to retry an operation after handling an error, in which case the error wouldn't propagate. Otherwise, the most common use for `ErrorLink` is logging.\n\n```ts\nimport { ErrorLink } from \"@apollo/client/link/error\";\n\nconst errorLink = new ErrorLink(({ error, operation, forward }) => {\n  if (someCondition(error)) {\n    // Retry the request, returning the new observable\n    return forward(operation);\n  }\n\n  // Log the error for any unhandled GraphQL errors or network errors.\n  console.log(`[Error]: ${error.message}`);\n\n  // If nothing is returned from the error handler callback, the error will be\n  // emitted from the link chain as normal.\n});\n```\n\n### Retry Link\n\nAlternatively, you can use the `RetryLink` from `@apollo/client/link/retry` to implement retry logic for failed operations.\n\n```typescript\nimport { RetryLink } from \"@apollo/client/link/retry\";\n\nconst retryLink = new RetryLink({\n  delay: {\n    initial: 300,\n    max: Infinity,\n    jitter: true,\n  },\n  attempts: {\n    max: 5,\n    retryIf: (error, operation) => {\n      // Retry on network errors\n      return !!error && operation.operationName !== \"SensitiveOperation\";\n    },\n  },\n});\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  link: from([retryLink, errorLink, httpLink]),\n});\n```\n\n### Custom Retry Logic\n\n```typescript\nconst retryLink = new RetryLink({\n  attempts: (count, operation, error) => {\n    // Don't retry mutations\n    if (\n      operation.query.definitions.some(\n        (def) =>\n          def.kind === \"OperationDefinition\" && def.operation === \"mutation\"\n      )\n    ) {\n      return false;\n    }\n\n    // Retry up to 3 times on network errors\n    return count < 3 && !!error;\n  },\n  delay: (count) => {\n    // Exponential backoff\n    return Math.min(1000 * Math.pow(2, count), 30000);\n  },\n});\n```\n\n## Error Boundaries\n\nWhen using suspenseful hooks, you should use React Error Boundaries for graceful error handling.\n\n### Non-suspense per-Component Error Handling\n\n```tsx\nimport {\n  CombinedGraphQLErrors,\n  ServerError,\n  ServerParseError,\n} from \"@apollo/client/errors\";\n\nfunction SafeUserList() {\n  const { data, error, loading, refetch } = useQuery(GET_USERS, {\n    errorPolicy: \"all\",\n    notifyOnNetworkStatusChange: true,\n  });\n\n  // Handle network errors\n  if (ServerError.is(error) || ServerParseError.is(error)) {\n    return (\n      <Alert severity=\"error\">\n        <AlertTitle>Connection Error</AlertTitle>\n        Failed to load users. Please check your internet connection.\n        <Button onClick={() => refetch()}>Retry</Button>\n      </Alert>\n    );\n  }\n\n  // Handle GraphQL errors but still show available data\n  return (\n    <div>\n      {CombinedGraphQLErrors.is(error) && (\n        <Alert severity=\"warning\">\n          Some data may be incomplete: {error.graphQLErrors[0].message}\n        </Alert>\n      )}\n\n      {loading && <LinearProgress />}\n\n      {data?.users && <UserList users={data.users} />}\n    </div>\n  );\n}\n```\n","isInternal":false,"tokens":2525,"sizeBytes":11094},{"name":"fragments.md","path":"docs/agent-skills/apollo-client/references/fragments.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/fragments.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Fragments Reference\n\nGraphQL fragments define a set of fields for a specific type. In Apollo Client, fragments are especially powerful when colocated with components to define each component's data requirements independently, creating a clear separation of concerns and enabling better component composition.\n\n## Table of Contents\n\n- [What Are Fragments](#what-are-fragments)\n- [Basic Fragment Syntax](#basic-fragment-syntax)\n- [Fragment Colocation](#fragment-colocation)\n- [Fragment Reading Hooks](#fragment-reading-hooks)\n- [Data Masking](#data-masking)\n- [Fragment Registry](#fragment-registry)\n- [TypeScript Integration](#typescript-integration)\n- [Best Practices](#best-practices)\n\n## What Are Fragments\n\nA GraphQL fragment defines a set of fields for a specific GraphQL type. Fragments are defined on a specific GraphQL type and can be included in operations using the spread operator (`...`).\n\nIn Apollo Client, fragments serve a specific purpose:\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment. This allows components to independently evolve their data requirements without creating artificial dependencies between unrelated parts of your application.\n\nFragments enable:\n\n1. **Component colocation**: Define the exact data requirements for a component alongside the component code\n2. **Independent evolution**: Change a component's data needs without affecting other components\n3. **Code organization**: Compose fragments together to build complete queries that mirror your component hierarchy\n\n## Basic Fragment Syntax\n\n### Defining a Fragment\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n    avatarUrl\n  }\n`;\n```\n\nEvery fragment includes:\n\n- A unique name (`UserFields`)\n- The type it operates on (`User`)\n- The fields to select\n\n### Using Fragments in Queries\n\nInclude fragments in queries using the spread operator:\n\n```typescript\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserFields\n    }\n  }\n\n  ${USER_FRAGMENT}\n`;\n```\n\nWhen using GraphQL Code Generator with the recommended configuration (typescript, typescript-operations, and typed-document-node plugins), fragments defined in your source files are automatically picked up and generated into typed document nodes. The generated fragment documents already include the fragment definition, so you don't need to interpolate them manually into queries.\n\n## Fragment Colocation\n\nFragment colocation is the practice of defining fragments in the same file as the component that uses them. This creates a clear contract between components and their data requirements.\n\n### Why Colocate Fragments\n\n- **Locality**: Data requirements live next to the code that uses them\n- **Maintainability**: Changes to component UI and data needs happen together\n- **Type safety**: TypeScript can infer exact types from colocated fragments\n- **Independence**: Components can evolve their data requirements without affecting other components\n\n### Colocation Pattern\n\nThe recommended pattern for colocating fragments with components:\n\n```tsx\nimport { gql, FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\n\n// Fragment definition\n// This will be picked up by Codegen to create `UserCard_UserFragmentDoc` in `./fragments.generated.ts`.\n// As that generated fragment document is correctly typed, we use that in the code going forward.\n// This fragment will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n}\n\n// This has been created from above fragment definition by CodeGen and is a correctly typed `TypedDocumentNode`\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated.ts\";\n\n// Component receives the (partially masked) parent object\nexport function UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a subscription to the fragment in the cache\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return (\n    <div>\n      <img src={data.avatarUrl} alt={data.name} />\n      <h2>{data.name}</h2>\n      <p>{data.email}</p>\n    </div>\n  );\n}\n```\n\n### Naming Convention\n\nA suggested naming pattern for fragments follows this convention:\n\n```text\n{ComponentName}_{propName}\n```\n\nWhere `propName` is the name of the prop the component receives containing the fragment data.\n\nExamples:\n\n- `UserCard_user` - Fragment for the `user` prop in the UserCard component\n- `PostList_posts` - Fragment for the `posts` prop in the PostList component\n- `CommentItem_comment` - Fragment for the `comment` prop in the CommentItem component\n\nThis convention makes it clear which component owns which fragment. However, you can choose a different naming convention based on your project's needs.\n\n**Note**: A component might accept fragment data through multiple props, in which case it would have multiple associated fragments. For example, a `CommentCard` component might accept both a `comment` prop and an `author` prop, resulting in `CommentCard_comment` and `CommentCard_author` fragments.\n\n### Composing Fragments\n\nParent components compose child fragments to build complete queries:\n\n```tsx\n// Child component\nimport { gql } from \"@apollo/client\";\n\nif (false) {\n  gql`\n    fragment UserAvatar_user on User {\n      id\n      avatarUrl\n      name\n    }\n  `;\n}\n\n// Parent component composes child fragments\nif (false) {\n  gql`\n    fragment UserProfile_user on User {\n      id\n      name\n      email\n      bio\n      ...UserAvatar_user\n    }\n  `;\n}\n\n// Page-level query composes all fragments\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserProfile_user\n      }\n    }\n  `;\n}\n```\n\nThis creates a hierarchy that mirrors your component tree.\n\n## Fragment Reading Hooks\n\nApollo Client provides hooks to read fragment data within components. These hooks work with data masking to ensure components only access the data they explicitly requested.\n\n### useSuspenseFragment\n\nFor components using Suspense and concurrent features:\n\n```tsx\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return <div>{data.name}</div>;\n}\n```\n\n### useFragment\n\nFor components not using Suspense:\n\n```tsx\nimport { useFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data, complete } = useFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  if (!complete) {\n    return <div>Loading...</div>;\n  }\n\n  return <div>{data.name}</div>;\n}\n```\n\nThe `complete` field indicates whether all fragment data is available in the cache.\n\n### Hook Options\n\nBoth hooks accept these options:\n\n```typescript\n{\n  // The fragment document (required)\n  fragment: TypedDocumentNode,\n\n  // The fragment name (optional in most cases)\n  // Only required if the fragment document contains multiple definitions\n  fragmentName?: string,\n\n  // The source data containing the fragment (required)\n  // Can be a single object or an array of objects\n  from: FragmentType<typeof fragment> | Array<FragmentType<typeof fragment>>,\n\n  // Variables for the fragment (optional)\n  variables?: Variables,\n}\n```\n\nWhen `from` is an array, the hook returns an array of results, allowing you to read fragments from multiple objects efficiently. **Note**: Array support for the `from` parameter was added in Apollo Client 4.1.0.\n\n## Data Masking\n\nData masking is a feature that prevents components from accessing data they didn't explicitly request through their fragments. This enforces proper data boundaries and prevents over-rendering.\n\n### Enabling Data Masking\n\nEnable data masking when creating your Apollo Client:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true, // Enable data masking\n});\n```\n\n### How Data Masking Works\n\nWith data masking enabled:\n\n1. Fragments return opaque `FragmentType` objects\n2. Components must use `useFragment` or `useSuspenseFragment` to unmask data\n3. Components can only access fields defined in their own fragments\n4. TypeScript enforces these boundaries at compile time\n\nWithout data masking:\n\n```tsx\n// ❌ Without data masking - component can access any data from parent\nfunction UserCard({ user }: { user: User }) {\n  // Can access any User field, even if not in fragment\n  return <div>{user.privateData}</div>;\n}\n```\n\nWith data masking:\n\n```tsx\n// ✅ With data masking - component can only access its fragment data\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // TypeScript error: 'privateData' doesn't exist on fragment type\n  // return <div>{data.privateData}</div>;\n\n  // Only fields from the fragment are accessible\n  return <div>{data.name}</div>;\n}\n```\n\n### Benefits of Data Masking\n\n- **Prevents over-rendering**: Components only re-render when their specific data changes\n- **Enforces boundaries**: Components can't accidentally depend on data they don't own\n- **Better refactoring**: Safe to modify parent queries without breaking child components\n- **Type safety**: TypeScript catches attempts to access unavailable fields\n\n## Fragment Registry\n\nThe fragment registry is an **alternative approach** to GraphQL Code Generator's automatic fragment inlining by name. It allows you to register fragments globally, making them available throughout your application by name reference.\n\n**Important**: GraphQL Code Generator automatically inlines fragments by name wherever they're used in your queries. Either approach is sufficient on its own—**you don't need to combine them**.\n\n### Creating a Fragment Registry\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createFragmentRegistry } from \"@apollo/client/cache\";\n\nexport const fragmentRegistry = createFragmentRegistry();\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache({\n    fragments: fragmentRegistry,\n  }),\n});\n```\n\n### Registering Fragments\n\nRegister fragments after defining them:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { fragmentRegistry } from \"./apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n  }\n`;\n\nfragmentRegistry.register(USER_FRAGMENT);\n```\n\nWith colocated fragments:\n\n```tsx\nimport { fragmentRegistry } from \"@/apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Register the fragment globally\nfragmentRegistry.register(UserCard_UserFragmentDoc);\n```\n\n### Using Registered Fragments\n\nOnce registered, fragments can be referenced by name in queries without explicit imports:\n\n```tsx\n// Fragment is available by name because it's registered\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserCard_user\n    }\n  }\n`;\n```\n\n### Approaches for Fragment Composition\n\nThere are three approaches to make child fragments available in parent queries:\n\n1. **GraphQL Code Generator inlining** (Recommended): CodeGen automatically inlines fragments by name. No manual work needed—just reference fragments by name in your queries.\n\n2. **Fragment Registry**: Manually register fragments to make them available by name. Useful for runtime scenarios where CodeGen isn't available.\n\n3. **Manual interpolation**: Explicitly import and interpolate child fragments into parent fragments:\n\n   ```typescript\n   import { CHILD_FRAGMENT } from \"./ChildComponent\";\n\n   const PARENT_FRAGMENT = gql`\n     fragment Parent_data on Data {\n       field\n       ...Child_data\n     }\n     ${CHILD_FRAGMENT}\n   `;\n   ```\n\n### Pros and Cons\n\n**GraphQL Code Generator inlining**:\n\n- ✅ Less work: Automatic, no manual registration needed\n- ❌ Larger bundle: Fragments are inlined into every query that uses them\n\n**Fragment Registry**:\n\n- ✅ Smaller bundle: Fragments are registered once, referenced by name\n- ❌ More work: Requires manual registration of each fragment\n- ❌ May cause issues with lazy-loaded modules if the module is not loaded before the query is executed\n- ✅ Best for deeply nested component trees where bundle size matters\n\n**Manual interpolation**:\n\n- ❌ Most work: Manual imports and interpolation required\n- ✅ Explicit: Clear fragment dependencies in code\n\n### Recommendation\n\nFor most applications using GraphQL Code Generator (as shown in this guide), **use the automatic inlining**—it requires no additional setup and works seamlessly. Consider the fragment registry only if bundle size becomes a concern in applications with deeply nested component trees.\n\n## TypeScript Integration\n\nApollo Client provides strong TypeScript support for fragments through GraphQL Code Generator.\n\n### Generated Types\n\nGraphQL Code Generator produces typed fragment documents:\n\n```typescript\n// Generated file: fragments.generated.ts\nexport type UserCard_UserFragment = {\n  __typename: \"User\";\n  id: string;\n  name: string;\n  email: string;\n  avatarUrl: string;\n} & { \" $fragmentName\"?: \"UserCard_UserFragment\" };\n\nexport const UserCard_UserFragmentDoc: TypedDocumentNode<\n  UserCard_UserFragment,\n  never\n>;\n```\n\n### Type-Safe Fragment Usage\n\nUse `FragmentType` to accept masked fragment data:\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // 'data' is fully typed as UserCard_UserFragment\n  return <div>{data.name}</div>;\n}\n```\n\n### Fragment Type Inference\n\nTypeScript infers types from fragment documents automatically:\n\n```tsx\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Types are inferred from the fragment\nconst { data } = useSuspenseFragment({\n  fragment: UserCard_UserFragmentDoc,\n  from: user,\n});\n\n// data.name is string\n// data.email is string\n// data.nonExistentField is a TypeScript error\n```\n\n### Parent-Child Type Safety\n\nWhen passing fragment data from parent to child:\n\n```tsx\n// Parent query\nconst { data } = useSuspenseQuery(GET_USER);\n\n// TypeScript ensures the query includes UserCard_user fragment\n// before allowing it to be passed to UserCard\n<UserCard user={data.user} />;\n```\n\n## Best Practices\n\n### Prefer Colocation Over Reuse\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment, even if multiple components currently need the same fields.\n\nSharing fragments between components just because they happen to need the same fields today creates artificial dependencies. When one component's requirements change, the shared fragment must be updated, causing all components using it to over-fetch data they don't need.\n\n```tsx\n// ✅ Good: Each component has its own fragment\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n    }\n  `;\n}\n\n// If UserCard later needs 'bio', only UserCard_user changes\n// UserListItem doesn't over-fetch 'bio'\n```\n\n```tsx\n// ❌ Avoid: Sharing a generic fragment across components\nconst COMMON_USER_FIELDS = gql`\n  fragment CommonUserFields on User {\n    id\n    name\n    email\n  }\n`;\n\n// UserCard and UserListItem both use CommonUserFields\n// When UserCard needs 'bio', adding it to CommonUserFields\n// causes UserListItem to over-fetch unnecessarily\n```\n\nThis independence allows each component to evolve its data requirements without affecting unrelated parts of your application.\n\n### One Query Per Page\n\nCompose all page data requirements into a single query at the page level:\n\n```tsx\n// ✅ Good: Single page-level query\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserHeader_user\n        ...UserPosts_user\n        ...UserFriends_user\n      }\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Multiple queries in different components\nfunction UserProfile() {\n  const { data: userData } = useQuery(GET_USER);\n  const { data: postsData } = useQuery(GET_USER_POSTS);\n  const { data: friendsData } = useQuery(GET_USER_FRIENDS);\n  // ...\n}\n```\n\n### Use Fragment-Reading Hooks in Components\n\nNon-page components should use `useFragment` or `useSuspenseFragment`:\n\n```tsx\n// ✅ Good: Component reads fragment data\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n  return <div>{data.name}</div>;\n}\n```\n\n```tsx\n// ❌ Avoid: Component uses query hook\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, { variables: { id: userId } });\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Request Only Required Fields\n\nKeep fragments minimal and only request fields the component actually uses:\n\n```tsx\n// ✅ Good: Only necessary fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Requesting unused fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n      bio\n      friends {\n        id\n        name\n      }\n      posts {\n        id\n        title\n      }\n    }\n  `;\n}\n```\n\n### Use @defer for Below-the-Fold Content\n\nThe `@defer` directive allows you to defer loading of non-critical fields, enabling faster initial page loads by prioritizing essential data. The deferred fields are delivered via incremental delivery and arrive after the non-deferred data, allowing the UI to progressively render as data becomes available.\n\nDefer slow fields that aren't immediately visible:\n\n```tsx\nif (false) {\n  gql`\n    query ProductPage($id: ID!) {\n      product(id: $id) {\n        id\n        name\n        price\n        ...ProductReviews_product @defer\n      }\n    }\n  `;\n}\n```\n\nThis allows the page to render quickly while reviews load in the background.\n\n### Handle Client-Only Fields\n\nUse the `@client` directive for fields resolved locally:\n\n```tsx\nif (false) {\n  gql`\n    fragment TodoItem_todo on Todo {\n      id\n      text\n      completed\n      isSelected @client\n    }\n  `;\n}\n```\n\n### Enable Data Masking for New Applications\n\nAlways enable data masking in new applications:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true,\n});\n```\n\nThis enforces proper boundaries from the start and prevents accidental coupling between components.\n\n## Apollo Client Data Masking vs GraphQL-Codegen Fragment Masking\n\nApollo Client's data masking and GraphQL Code Generator's fragment masking are different features that serve different purposes:\n\n### GraphQL-Codegen Fragment Masking\n\nGraphQL Code Generator's fragment masking (when using the client preset) is a **type-level** feature:\n\n- Masks data only at the TypeScript type level\n- The actual runtime data remains fully accessible on the object\n- Using their `useFragment` hook simply \"unmasks\" the data on a type level\n- Does not prevent accidental access to data at runtime\n- Parent components receive all data and pass it down\n- This means the parent component has to be subscribed to all data\n\n### Apollo Client Data Masking\n\nApollo Client's data masking is a **runtime** feature with significant performance benefits:\n\n- Removes data at the runtime level, not just the type level\n- The `useFragment` and `useSuspenseFragment` hooks create cache subscriptions\n- Parent objects are sparse and only contain unmasked data\n- Prevents accidental access to data that should be masked\n\n### Key Benefits of Apollo Client Data Masking\n\n**1. No Accidental Data Access**\n\nWith runtime data masking, masked fields are not present in the parent object at all. You cannot accidentally access them, even if you bypass TypeScript type checking.\n\n**2. Fewer Re-renders**\n\nApollo Client's approach creates more efficient subscriptions:\n\n- **Without data masking**: Parent component subscribes to all fields (including masked ones). When a masked child field changes, the parent re-renders to pass that runtime data down the tree.\n- **With data masking**: Parent component only subscribes to its own unmasked fields. Subscriptions on masked fields happen lower in the React component tree when the child component calls `useSuspenseFragment`. When a masked field changes, only the child component that subscribed to it re-renders.\n\n### Example\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseQuery, useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction ParentComponent() {\n  const { data } = useSuspenseQuery(GET_USER);\n\n  // With Apollo Client data masking:\n  // - data.user only contains unmasked fields\n  // - Parent doesn't re-render when child-specific fields change\n\n  return <UserCard user={data.user} />;\n}\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a cache subscription specifically for UserCard_user fields\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // Only this component re-renders when these fields change\n  return <div>{data.name}</div>;\n}\n```\n\nThis granular subscription approach improves performance in large applications with deeply nested component trees.\n","isInternal":false,"tokens":4955,"sizeBytes":22674},{"name":"integration-client.md","path":"docs/agent-skills/apollo-client/references/integration-client.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/integration-client.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Apollo Client Integration for Client-Side Apps\n\nThis guide covers setting up Apollo Client in client-side React applications without server-side rendering (SSR). This includes applications using Vite, Parcel, Create React App, or other bundlers that don't implement SSR.\n\nFor applications with SSR, use one of the framework-specific integration guides instead:\n\n- [Next.js App Router](integration-nextjs.md)\n- [React Router Framework Mode](integration-react-router.md)\n- [TanStack Start](integration-tanstack-start.md)\n\n## Installation\n\n```bash\nnpm install @apollo/client graphql rxjs\n```\n\n## TypeScript Code Generation (optional but recommended)\n\nFor type-safe GraphQL operations with TypeScript, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup Steps\n\n### Step 1: Create Client\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\n\n// Recommended: Use HttpOnly cookies for authentication\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  credentials: \"include\", // Sends cookies with requests (secure when using HttpOnly cookies)\n});\n\nconst client = new ApolloClient({\n  link: httpLink,\n  cache: new InMemoryCache(),\n});\n```\n\nIf you need manual token management (less secure, only when HttpOnly cookies aren't available):\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\n\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n});\n\nconst authLink = new SetContextLink(({ headers }) => {\n  const token = localStorage.getItem(\"token\");\n  return {\n    headers: {\n      ...headers,\n      authorization: token ? `Bearer ${token}` : \"\",\n    },\n  };\n});\n\nconst client = new ApolloClient({\n  link: authLink.concat(httpLink),\n  cache: new InMemoryCache(),\n});\n```\n\n### Step 2: Setup Provider\n\n```tsx\nimport { ApolloProvider } from \"@apollo/client\";\nimport App from \"./App\";\n\nfunction Root() {\n  return (\n    <ApolloProvider client={client}>\n      <App />\n    </ApolloProvider>\n  );\n}\n```\n\n### Step 3: Execute Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USERS = gql`\n  query GetUsers {\n    users {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserList() {\n  const { loading, error, data, dataState } = useQuery(GET_USERS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Basic Query Usage\n\n### Using Variables\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n> **Note for TypeScript users**: Use [`dataState`](https://www.apollographql.com/docs/react/data/typescript#type-narrowing-data-with-datastate) for more robust type safety and better type narrowing in Apollo Client 4.x.\n\n### TypeScript Integration\n\nFor complete examples with loading, error handling, and `dataState` for type narrowing, see [Basic Query Usage](#basic-query-usage) above.\n\n#### Usage with Generated Types\n\nFor type-safe operations with code generation, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\nQuick example:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\n// Define your query with the if (false) pattern for code generation\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n#### Usage with Manual Type Annotations\n\nIf not using code generation, define types alongside your queries using `TypedDocumentNode`:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetUserData {\n  user: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface GetUserVariables {\n  id: string;\n}\n\n// Types should always be defined via TypedDocumentNode alongside your queries/mutations, not at the useQuery/useMutation call site\nconst GET_USER: TypedDocumentNode<GetUserData, GetUserVariables> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // data.user is automatically typed from GET_USER\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Basic Mutation Usage\n\n```tsx\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\ninterface CreateUserMutation {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserMutationVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<\n  CreateUserMutation,\n  CreateUserMutationVariables\n> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (formData: FormData) => {\n    const { data } = await createUser({\n      variables: {\n        input: {\n          name: formData.get(\"name\") as string,\n          email: formData.get(\"email\") as string,\n        },\n      },\n    });\n    if (data) {\n      console.log(\"Created user:\", data.createUser);\n    }\n  };\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <input name=\"name\" placeholder=\"Name\" />\n      <input name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create User\"}\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n## Client Configuration Options\n\n```typescript\nconst client = new ApolloClient({\n  // Required: The cache implementation\n  cache: new InMemoryCache({\n    typePolicies: {\n      Query: {\n        fields: {\n          // Field-level cache configuration\n        },\n      },\n    },\n  }),\n\n  // Network layer\n  link: new HttpLink({ uri: \"/graphql\" }),\n\n  // Avoid defaultOptions if possible as they break TypeScript expectations.\n  // Configure options per-query/mutation instead for better type safety.\n  // defaultOptions: {\n  //   watchQuery: { fetchPolicy: 'cache-and-network' },\n  // },\n\n  // DevTools are enabled by default in development\n  // Only configure when enabling in production\n  devtools: {\n    enabled: true, // Only needed for production\n  },\n\n  // Custom name for this client instance\n  clientAwareness: {\n    name: \"web-client\",\n    version: \"1.0.0\",\n  },\n});\n```\n\n## Important Considerations\n\n1. **Choose Your Hook Strategy:** Decide if your application should be based on Suspense. If it is, use suspenseful hooks like `useSuspenseQuery` (see [Suspense Hooks guide](suspense-hooks.md)), otherwise use non-suspenseful hooks like `useQuery` (see [Queries guide](queries.md)).\n\n2. **Client-Side Only:** This setup is for client-side apps without SSR. The Apollo Client instance is created once and reused throughout the application lifecycle.\n\n3. **Authentication:** Prefer HttpOnly cookies with `credentials: \"include\"` in `HttpLink` options to avoid exposing tokens to JavaScript. If manual token management is necessary, use `SetContextLink` to dynamically add authentication headers from `localStorage` or other client-side storage.\n\n4. **Environment Variables:** Store your GraphQL endpoint URL in environment variables for different environments (development, staging, production).\n\n5. **Error Handling:** Always handle `loading` and `error` states when using `useQuery` or `useLazyQuery`. For Suspense-based hooks (`useSuspenseQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n","isInternal":false,"tokens":2065,"sizeBytes":8858},{"name":"integration-nextjs.md","path":"docs/agent-skills/apollo-client/references/integration-nextjs.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/integration-nextjs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Apollo Client Integration with Next.js App Router\n\nThis guide covers integrating Apollo Client in a Next.js application using the App Router architecture with support for both React Server Components (RSC) and Client Components.\n\n## What is supported?\n\n### React Server Components\n\nApollo Client provides a shared client instance across all server components for a single request, preventing duplicate GraphQL requests and optimizing server-side rendering.\n\n### React Client Components\n\nWhen using the `app` directory, client components are rendered both on the server (SSR) and in the browser. Apollo Client enables you to execute GraphQL queries on the server and use the results to hydrate your browser-side cache, delivering fully-rendered pages to users.\n\n## Installation\n\nInstall Apollo Client and the Next.js integration package:\n\n```bash\nnpm install @apollo/client@latest @apollo/client-integration-nextjs graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup for React Server Components (RSC)\n\n### Step 1: Create Apollo Client Configuration\n\nCreate an `ApolloClient.ts` file in your app directory:\n\n```typescript\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  registerApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nexport const { getClient, query, PreloadQuery } = registerApolloClient(() => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({\n      // Use an absolute URL for SSR (relative URLs cannot be used in SSR)\n      uri: \"https://your-api.com/graphql\",\n      fetchOptions: {\n        // Optional: Next.js-specific fetch options for caching and revalidation\n        // See: https://nextjs.org/docs/app/api-reference/functions/fetch\n      },\n    }),\n  });\n});\n```\n\n### Step 2: Use in Server Components\n\nYou can now use the `getClient` function or the `query` shortcut in your server components:\n\n```typescript\nimport { query } from \"./ApolloClient\";\n\nasync function UserProfile({ userId }: { userId: string }) {\n  const { data } = await query({\n    query: GET_USER,\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Override Next.js Fetch Options\n\nYou can override Next.js-specific `fetch` options per query using `context.fetchOptions`:\n\n```typescript\nconst { data } = await getClient().query({\n  query: GET_USER,\n  context: {\n    fetchOptions: {\n      next: { revalidate: 60 }, // Revalidate every 60 seconds\n    },\n  },\n});\n```\n\n## Setup for Client Components (SSR and Browser)\n\n### Step 1: Create Apollo Wrapper Component\n\nCreate `app/ApolloWrapper.tsx`:\n\n```typescript\n\"use client\";\n\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  ApolloNextAppProvider,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nfunction makeClient() {\n  const httpLink = new HttpLink({\n    // Use an absolute URL for SSR\n    uri: \"https://your-api.com/graphql\",\n    fetchOptions: {\n      // Optional: Next.js-specific fetch options\n      // Note: This doesn't work with `export const dynamic = \"force-static\"`\n    },\n  });\n\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: httpLink,\n  });\n}\n\nexport function ApolloWrapper({ children }: React.PropsWithChildren) {\n  return (\n    <ApolloNextAppProvider makeClient={makeClient}>\n      {children}\n    </ApolloNextAppProvider>\n  );\n}\n```\n\n### Step 2: Wrap Root Layout\n\nWrap your `RootLayout` in the `ApolloWrapper` component in `app/layout.tsx`:\n\n```typescript\nimport { ApolloWrapper } from \"./ApolloWrapper\";\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <ApolloWrapper>{children}</ApolloWrapper>\n      </body>\n    </html>\n  );\n}\n```\n\n> **Note:** This works even if your layout is a React Server Component. It ensures all Client Components share the same Apollo Client instance through `ApolloNextAppProvider`.\n\n### Step 3: Use Apollo Client Hooks in Client Components\n\nFor optimal streaming SSR, use suspense-enabled hooks like `useSuspenseQuery` and `useFragment`:\n\n```typescript\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Preloading Data from RSC to Client Components\n\nYou can preload data in React Server Components to populate the cache of your Client Components.\n\n### Step 1: Use PreloadQuery in Server Components\n\n```tsx\nimport { PreloadQuery } from \"./ApolloClient\";\nimport { Suspense } from \"react\";\n\nexport default async function Page() {\n  return (\n    <PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n      <Suspense fallback={<>Loading...</>}>\n        <ClientChild />\n      </Suspense>\n    </PreloadQuery>\n  );\n}\n```\n\n### Step 2: Consume with useSuspenseQuery in Client Components\n\n```tsx\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function ClientChild() {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n> **Important:** Data fetched this way should be considered client data and never referenced in Server Components. `PreloadQuery` prevents mixing server data and client data by creating a separate `ApolloClient` instance.\n\n### Using with useReadQuery\n\nFor advanced use cases, you can use `PreloadQuery` with `useReadQuery` to avoid request waterfalls:\n\n```tsx\n<PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n  {(queryRef) => (\n    <Suspense fallback={<>Loading...</>}>\n      <ClientChild queryRef={queryRef} />\n    </Suspense>\n  )}\n</PreloadQuery>\n```\n\nIn your Client Component:\n\n```tsx\n\"use client\";\n\nimport {\n  useQueryRefHandlers,\n  useReadQuery,\n  QueryRef,\n} from \"@apollo/client/react\";\n\nexport function ClientChild({ queryRef }: { queryRef: QueryRef<TQueryData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Handling Multipart Responses (@defer) in SSR\n\nWhen using the `@defer` directive, `useSuspenseQuery` will only suspend until the initial response is received. To handle deferred data properly, you have three strategies:\n\n### Strategy 1: Use PreloadQuery with useReadQuery\n\n`PreloadQuery` allows deferred data to be fully transported and streamed chunk-by-chunk.\n\n### Strategy 2: Remove @defer Fragments\n\nUse `RemoveMultipartDirectivesLink` to strip `@defer` directives from queries during SSR:\n\n```typescript\nimport { RemoveMultipartDirectivesLink } from \"@apollo/client-integration-nextjs\";\n\nnew RemoveMultipartDirectivesLink({\n  stripDefer: true, // Default: true\n});\n```\n\nYou can exclude specific fragments from stripping by labeling them:\n\n```graphql\nquery myQuery {\n  fastField\n  ... @defer(label: \"SsrDontStrip1\") {\n    slowField1\n  }\n}\n```\n\n### Strategy 3: Wait for Deferred Data\n\nUse `AccumulateMultipartResponsesLink` to debounce the initial response:\n\n```typescript\nimport { AccumulateMultipartResponsesLink } from \"@apollo/client-integration-nextjs\";\n\nnew AccumulateMultipartResponsesLink({\n  cutoffDelay: 100, // Wait up to 100ms for incremental data\n});\n```\n\n### Combined Approach: SSRMultipartLink\n\nCombine both strategies with `SSRMultipartLink`:\n\n```typescript\nimport { SSRMultipartLink } from \"@apollo/client-integration-nextjs\";\n\nnew SSRMultipartLink({\n  stripDefer: true,\n  cutoffDelay: 100,\n});\n```\n\n## Testing\n\nReset singleton instances between tests using the `resetApolloClientSingletons` helper:\n\n```typescript\nimport { resetApolloClientSingletons } from \"@apollo/client-integration-nextjs\";\n\nafterEach(resetApolloClientSingletons);\n```\n\n## Debugging\n\nEnable verbose logging in your `app/ApolloWrapper.tsx`:\n\n```typescript\nimport { setLogVerbosity } from \"@apollo/client\";\n\nsetLogVerbosity(\"debug\");\n```\n\n## Important Considerations\n\n1. **Separate RSC and SSR Queries:** Avoid overlapping queries between RSC and SSR. RSC queries don't update in the browser, while SSR queries can update dynamically as the cache changes.\n\n2. **Use Absolute URLs:** Always use absolute URLs in `HttpLink` for SSR, as relative URLs cannot be used in server-side rendering.\n\n3. **Streaming SSR:** For optimal performance, use `useSuspenseQuery` and `useFragment` to take advantage of React 18's streaming SSR capabilities.\n\n4. **Suspense Boundaries:** Place `Suspense` boundaries at meaningful places in your UI for the best user experience.\n","isInternal":false,"tokens":2013,"sizeBytes":8660},{"name":"integration-react-router.md","path":"docs/agent-skills/apollo-client/references/integration-react-router.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/integration-react-router.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Apollo Client Integration with React Router Framework Mode\n\nThis guide covers integrating Apollo Client in a React Router 7 application with support for modern streaming SSR.\n\n## Installation\n\nInstall Apollo Client and the React Router integration package:\n\n```bash\nnpm install @apollo/client-integration-react-router @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Create Apollo Configuration\n\nCreate an `app/apollo.ts` file that exports a `makeClient` function and an `apolloLoader`:\n\n```typescript\nimport { HttpLink, InMemoryCache } from \"@apollo/client\";\nimport {\n  createApolloLoaderHandler,\n  ApolloClient,\n} from \"@apollo/client-integration-react-router\";\n\n// `request` will be available on the server during SSR or in loaders, but not in the browser\nexport const makeClient = (request?: Request) => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n};\n\nexport const apolloLoader = createApolloLoaderHandler(makeClient);\n```\n\n> **Important:** `ApolloClient` must be imported from `@apollo/client-integration-react-router`, not from `@apollo/client`.\n\n### Step 2: Reveal Entry Files\n\nRun the following command to create the entry files if they don't exist:\n\n```bash\nnpx react-router reveal\n```\n\nThis will create `app/entry.client.tsx` and `app/entry.server.tsx`.\n\n### Step 3: Configure Client Entry\n\nAdjust `app/entry.client.tsx` to wrap your app in `ApolloProvider`:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\nimport { StrictMode, startTransition } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nstartTransition(() => {\n  const client = makeClient();\n  hydrateRoot(\n    document,\n    <StrictMode>\n      <ApolloProvider client={client}>\n        <HydratedRouter />\n      </ApolloProvider>\n    </StrictMode>\n  );\n});\n```\n\n### Step 4: Configure Server Entry\n\nAdjust `app/entry.server.tsx` to wrap your app in `ApolloProvider` during SSR:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\n// ... other imports\n\nexport default function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  routerContext: EntryContext\n) {\n  return new Promise((resolve, reject) => {\n    // ... existing code\n\n    const client = makeClient(request);\n\n    const { pipe, abort } = renderToPipeableStream(\n      <ApolloProvider client={client}>\n        <ServerRouter\n          context={routerContext}\n          url={request.url}\n          abortDelay={ABORT_DELAY}\n        />\n      </ApolloProvider>,\n      {\n        [readyOption]() {\n          shellRendered = true;\n          // ... rest of the handler\n        },\n        // ... other options\n      }\n    );\n  });\n}\n```\n\n### Step 5: Add Hydration Helper\n\nAdd `<ApolloHydrationHelper>` to `app/root.tsx`:\n\n```typescript\nimport { ApolloHydrationHelper } from \"@apollo/client-integration-react-router\";\nimport { Links, Meta, Outlet, Scripts, ScrollRestoration } from \"react-router\";\n\nexport function Layout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <Meta />\n        <Links />\n      </head>\n      <body>\n        <ApolloHydrationHelper>{children}</ApolloHydrationHelper>\n        <ScrollRestoration />\n        <Scripts />\n      </body>\n    </html>\n  );\n}\n\nexport default function App() {\n  return <Outlet />;\n}\n```\n\n## Usage\n\n### Using apolloLoader with useReadQuery\n\nYou can now use the `apolloLoader` function to create Apollo-enabled loaders for your routes:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef } = useLoaderData<typeof loader>();\n  const { data } = useReadQuery(userQueryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n> **Important:** To provide better TypeScript support, `apolloLoader` is a method that you need to call twice: `apolloLoader<LoaderArgs>()(loader)`\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const postsQueryRef = preloadQuery(GET_POSTS, {\n    variables: { userId: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n    postsQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef, postsQueryRef } = useLoaderData<typeof loader>();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: postsData } = useReadQuery(postsQueryRef);\n\n  return (\n    <div>\n      <h1>{userData.user.name}</h1>\n      <h2>Posts</h2>\n      <ul>\n        {postsData.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import ApolloClient from Integration Package:** Always import `ApolloClient` from `@apollo/client-integration-react-router`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **TypeScript Support:** The `apolloLoader` function requires double invocation for proper TypeScript type inference: `apolloLoader<LoaderArgs>()(loader)`.\n\n3. **Request Context:** The `makeClient` function receives the `Request` object during SSR and in loaders, but not in the browser. Use this to set up auth headers or other request-specific configuration.\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Hydration:** The `ApolloHydrationHelper` component ensures that data loaded on the server is properly hydrated on the client, preventing unnecessary refetches.\n","isInternal":false,"tokens":1684,"sizeBytes":7121},{"name":"integration-tanstack-start.md","path":"docs/agent-skills/apollo-client/references/integration-tanstack-start.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/integration-tanstack-start.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Apollo Client Integration with TanStack Start\n\nThis guide covers integrating Apollo Client in a TanStack Start application with support for modern streaming SSR.\n\n> **Note:** When using `npx create-tsrouter-app` to create a new TanStack Start application, you can choose Apollo Client in the setup wizard to have all of this configuration automatically set up for you.\n\n## Installation\n\nInstall Apollo Client and the TanStack Start integration package:\n\n```bash\nnpm install @apollo/client-integration-tanstack-start @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Configure Root Route with Context\n\nIn your `routes/__root.tsx`, change from `createRootRoute` to `createRootRouteWithContext` to provide the right context type:\n\n```typescript\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\nimport {\n  createRootRouteWithContext,\n  Outlet,\n} from \"@tanstack/react-router\";\n\nexport const Route = createRootRouteWithContext<ApolloClientIntegration.RouterContext>()({\n  component: RootComponent,\n});\n\nfunction RootComponent() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Outlet />\n      </body>\n    </html>\n  );\n}\n```\n\n### Step 2: Set Up Apollo Client in Router\n\nIn your `router.tsx`, set up your Apollo Client instance and run `routerWithApolloClient`:\n\n```typescript\nimport {\n  routerWithApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** `ApolloClient` and `InMemoryCache` must be imported from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`.\n\n## Usage\n\n### Option 1: Loader with preloadQuery and useReadQuery\n\nUse the `preloadQuery` function in your route loader to preload data during navigation:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/user/$userId\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery }, params }) => {\n    const queryRef = preloadQuery(GET_USER, {\n      variables: { id: params.userId },\n    });\n\n    return {\n      queryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { queryRef } = Route.useLoaderData();\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Option 2: Direct useSuspenseQuery in Component\n\nYou can also use Apollo Client's suspenseful hooks directly in your component without a loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_POSTS: TypedDocumentNode<{\n  posts: Array<{ id: string; title: string; content: string }>;\n}> = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n      content\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/posts\")({\n  component: RouteComponent,\n});\n\nfunction RouteComponent() {\n  const { data } = useSuspenseQuery(GET_POSTS);\n\n  return (\n    <div>\n      <h1>Posts</h1>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>\n            <h2>{post.title}</h2>\n            <p>{post.content}</p>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const Route = createFileRoute(\"/dashboard\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery } }) => {\n    const userQueryRef = preloadQuery(GET_USER, {\n      variables: { id: \"current\" },\n    });\n\n    const statsQueryRef = preloadQuery(GET_STATS, {\n      variables: { period: \"month\" },\n    });\n\n    return {\n      userQueryRef,\n      statsQueryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { userQueryRef, statsQueryRef } = Route.useLoaderData();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: statsData } = useReadQuery(statsQueryRef);\n\n  return (\n    <div>\n      <h1>Welcome, {userData.user.name}</h1>\n      <div>\n        <h2>Monthly Stats</h2>\n        <p>Views: {statsData.stats.views}</p>\n        <p>Clicks: {statsData.stats.clicks}</p>\n      </div>\n    </div>\n  );\n}\n```\n\n### Using useQueryRefHandlers for Refetching\n\nWhen using `useReadQuery`, you can get refetch functionality from `useQueryRefHandlers`:\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n```typescript\nimport { useReadQuery, useQueryRefHandlers, QueryRef } from \"@apollo/client/react\";\n\nfunction UserComponent({ queryRef }: { queryRef: QueryRef<GetUserQuery> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <button onClick={() => refetch()}>Refresh</button>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import from Integration Package:** Always import `ApolloClient` and `InMemoryCache` from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **Context Type:** Use `createRootRouteWithContext<ApolloClientIntegration.RouterContext>()` to provide proper TypeScript types for the `preloadQuery` function in loaders.\n\n3. **Loader vs Component Queries:**\n\n   - Use `preloadQuery` in loaders when you want to start fetching data before the component renders\n   - Use `useSuspenseQuery` directly in components for simpler cases or when data fetching can wait until render\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Management:** The Apollo Client instance is shared across all routes, so cache updates from one route will be reflected in all routes that use the same data.\n\n6. **Authentication:** Use Apollo Client's `SetContextLink` for dynamic auth tokens.\n\n## Advanced Configuration\n\n### Adding Authentication\n\nFor authentication in TanStack Start with SSR support, you need to handle both server and client environments differently. Use `createIsomorphicFn` to provide environment-specific implementations:\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n  routerWithApolloClient,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { ApolloLink, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\nimport { createIsomorphicFn } from \"@tanstack/react-start\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { getSession, getCookie } from \"@tanstack/react-start/server\";\nimport { routeTree } from \"./routeTree.gen\";\n\n// Create isomorphic link that uses different implementations per environment\nconst createAuthLink = createIsomorphicFn()\n  .server(() => {\n    // Server-only: Can access server-side functions like `getCookies`, `getCookie`, `getSession`, etc. exported from `\"@tanstack/react-start/server\"`\n    return new SetContextLink(async (prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: getCookie(\"Authorization\"),\n        },\n      };\n    });\n  })\n  .client(() => {\n    // Client-only: Can access `localStorage` or other browser APIs\n    return new SetContextLink((prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: localStorage.getItem(\"authToken\") ?? \"\",\n        },\n      };\n    });\n  });\n\nexport function getRouter() {\n  const httpLink = new HttpLink({\n    uri: \"https://your-graphql-endpoint.com/graphql\",\n  });\n\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: ApolloLink.from([createAuthLink(), httpLink]),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** The `getRouter` function is called both on the server and client, so it must not contain environment-specific code. Use `createIsomorphicFn` to provide different implementations:\n>\n> - **Server:** Can access server-only functions like `getSession`, `getCookies`, `getCookie` from `@tanstack/react-start/server` to access authentication information in request or session data\n> - **Client:** Can use `localStorage` or other browser APIs to access auth tokens (if setting `credentials: \"include\"` is sufficient, try to prefer that over manually setting auth headers client-side)\n>\n> This ensures your authentication works correctly in both SSR and browser contexts.\n\n### Custom Cache Configuration\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\nimport { routerWithApolloClient } from \"@apollo/client-integration-tanstack-start\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache({\n      typePolicies: {\n        Query: {\n          fields: {\n            posts: {\n              merge(existing = [], incoming) {\n                return [...existing, ...incoming];\n              },\n            },\n          },\n        },\n      },\n    }),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n","isInternal":false,"tokens":2581,"sizeBytes":11164},{"name":"mutations.md","path":"docs/agent-skills/apollo-client/references/mutations.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/mutations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Mutations Reference\n\n## Table of Contents\n\n- [useMutation Hook](#usemutation-hook)\n- [Mutation Variables](#mutation-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [Optimistic UI](#optimistic-ui)\n- [Cache Updates](#cache-updates)\n- [Refetch Queries](#refetch-queries)\n- [Error Handling](#error-handling)\n\n## useMutation Hook\n\nThe `useMutation` hook is used to execute GraphQL mutations.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst ADD_TODO = gql`\n  mutation AddTodo($text: String!) {\n    addTodo(text: $text) {\n      id\n      text\n      completed\n    }\n  }\n`;\n\nfunction AddTodo() {\n  const [addTodo, { data, loading, error }] = useMutation(ADD_TODO);\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        const form = e.currentTarget;\n        const text = new FormData(form).get(\"text\") as string;\n        addTodo({ variables: { text } });\n        form.reset();\n      }}\n    >\n      <input name=\"text\" placeholder=\"Add todo\" />\n      <button type=\"submit\" disabled={loading}>\n        Add\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n### Return Tuple\n\n```typescript\nconst [\n  mutateFunction, // Function to call to execute mutation\n  {\n    data, // Mutation result data\n    loading, // True while mutation is in flight\n    error, // ApolloError if mutation failed\n    called, // True if mutation has been called\n    reset, // Reset mutation state\n    client, // Apollo Client instance\n  },\n] = useMutation(MUTATION);\n```\n\n## Mutation Variables\n\n### Variables in Options\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  variables: {\n    input: {\n      name: \"Default User\",\n      email: \"default@example.com\",\n    },\n  },\n});\n\n// Call with default variables\nawait createUser();\n\n// Override variables\nawait createUser({\n  variables: {\n    input: {\n      name: \"Custom User\",\n      email: \"custom@example.com\",\n    },\n  },\n});\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\n\ninterface CreateUserData {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<CreateUserData, CreateUserVariables> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nconst [createUser, { data, loading }] = useMutation(CREATE_USER);\n\nconst { data } = await createUser({\n  variables: {\n    input: { name: \"John\", email: \"john@example.com\" },\n  },\n});\n\n// data.createUser is fully typed\n```\n\n## Loading and Error States\n\n### Handling in UI\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading, error, data, reset }] =\n    useMutation(CREATE_POST);\n\n  if (data) {\n    return (\n      <div>\n        <p>Post created: {data.createPost.title}</p>\n        <button onClick={reset}>Create another</button>\n      </div>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <input name=\"title\" disabled={loading} />\n      <textarea name=\"content\" disabled={loading} />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n      {error && (\n        <div className=\"error\">\n          <p>Failed to create post: {error.message}</p>\n          <button onClick={reset}>Try again</button>\n        </div>\n      )}\n    </form>\n  );\n}\n```\n\n### Async/Await Pattern\n\nIf you only need the promise without using the hook's loading/data state, use `client.mutate` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction CreatePost() {\n  const client = useApolloClient();\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await client.mutate({\n        mutation: CREATE_POST,\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      ...\n    </form>\n  );\n}\n```\n\nIf you do use the hook's state, e.g. because you want to render the `loading` state, errors or returned `data`, you can also use the `useMutation` hook with `async..await` in your handler:\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading }] = useMutation(CREATE_POST);\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await createPost({\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n    </form>\n  );\n}\n```\n\n## Optimistic UI\n\nOptimistic UI immediately reflects the expected result of a mutation before the server responds.\n\n### Basic Optimistic Response\n\n**Important**: `optimisticResponse` needs to be a full valid response for the mutation. A partial result might result in subtle errors.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  optimisticResponse: {\n    addTodo: {\n      __typename: \"Todo\",\n      id: \"temp-id\",\n      text: \"New todo\",\n      completed: false,\n    },\n  },\n});\n```\n\n### Dynamic Optimistic Response\n\n```tsx\nfunction TodoList() {\n  const [addTodo] = useMutation(ADD_TODO);\n\n  const handleAdd = (text: string) => {\n    addTodo({\n      variables: { text },\n      optimisticResponse: {\n        addTodo: {\n          __typename: \"Todo\",\n          id: `temp-${Date.now()}`,\n          text,\n          completed: false,\n        },\n      },\n    });\n  };\n\n  return <AddTodoForm onAdd={handleAdd} />;\n}\n```\n\n### Optimistic Response with Cache Update\n\n```tsx\nconst [toggleTodo] = useMutation(TOGGLE_TODO, {\n  optimisticResponse: ({ id }) => ({\n    toggleTodo: {\n      __typename: \"Todo\",\n      id,\n      completed: true, // Assume success\n    },\n  }),\n  update: (cache, { data }) => {\n    // This runs twice: once with optimistic data, once with server data\n    cache.modify({\n      id: cache.identify(data.toggleTodo),\n      fields: {\n        completed: () => data.toggleTodo.completed,\n      },\n    });\n  },\n});\n```\n\n## Cache Updates\n\n### Using update Function\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Read existing todos from cache\n    const existingTodos = cache.readQuery<{ todos: Todo[] }>({\n      query: GET_TODOS,\n    });\n\n    // Write updated list back to cache\n    cache.writeQuery({\n      query: GET_TODOS,\n      data: {\n        todos: [...(existingTodos?.todos ?? []), data.addTodo],\n      },\n    });\n  },\n});\n```\n\n### cache.modify\n\n```tsx\nconst [deleteTodo] = useMutation(DELETE_TODO, {\n  update: (cache, { data }) => {\n    cache.modify({\n      fields: {\n        todos: (existingTodos: Reference[], { readField }) => {\n          return existingTodos.filter(\n            (todoRef) => readField(\"id\", todoRef) !== data.deleteTodo.id\n          );\n        },\n      },\n    });\n  },\n});\n```\n\n### cache.evict\n\n```tsx\nconst [deleteUser] = useMutation(DELETE_USER, {\n  update: (cache, { data }) => {\n    // Remove the user object from cache entirely\n    cache.evict({ id: cache.identify(data.deleteUser) });\n    // Clean up dangling references\n    cache.gc();\n  },\n});\n```\n\n### Updating Related Queries\n\n```tsx\nconst [createPost] = useMutation(CREATE_POST, {\n  update: (cache, { data }) => {\n    // Update author's post count\n    cache.modify({\n      id: cache.identify({ __typename: \"User\", id: data.createPost.authorId }),\n      fields: {\n        postCount: (existing) => existing + 1,\n        posts: (existing, { toReference }) => [\n          ...existing,\n          toReference(data.createPost),\n        ],\n      },\n    });\n\n    // Add to feed\n    cache.modify({\n      fields: {\n        feed: (existing, { toReference }) => [\n          toReference(data.createPost),\n          ...existing,\n        ],\n      },\n    });\n  },\n});\n```\n\n## Refetch Queries\n\n### Basic Refetch\n\nThere are three refetch notations:\n\n- **String**: `refetchQueries: ['getTodos']` - refetches all active `getTodos` queries\n- **Query document**: `refetchQueries: [GET_TODOS]` - refetches all active queries using this document\n- **Object**: `refetchQueries: [{ query: GET_TODOS }, { query: GET_TODOS, variables: { page: 25 } }]` - **fetches** the query, regardless if it's actively used in the UI\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  // Refetch all active GET_TODOS queries\n  refetchQueries: [\"getTodos\"],\n  // Or: refetchQueries: [GET_TODOS],\n});\n\n// Fetch specific query with variables (even if not active)\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }, { query: GET_TODO_COUNT }],\n});\n```\n\n### Conditional Refetch\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: (result) => {\n    if (result.data?.addTodo.priority === \"HIGH\") {\n      return [{ query: GET_HIGH_PRIORITY_TODOS }];\n    }\n    return [{ query: GET_TODOS }];\n  },\n});\n```\n\n### Refetch Active Queries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: \"active\", // Refetch all active queries\n  // Or: 'all' to refetch all queries (including inactive)\n});\n```\n\n### awaitRefetchQueries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }],\n  awaitRefetchQueries: true, // Wait for refetch before resolving mutation\n});\n```\n\n### onQueryUpdated\n\nReturning `true` from `onQueryUpdated` causes a refetch. Don't call `refetch()` manually inside `onQueryUpdated`, as it won't retain the query and might cancel it early.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Update cache...\n  },\n  onQueryUpdated: (observableQuery) => {\n    // Called for each query affected by cache update\n    console.log(`Query ${observableQuery.queryName} was updated`);\n    // Return true to refetch\n    return true;\n  },\n});\n```\n\n## Error Handling\n\n### Error Policy\n\n```tsx\nconst [createUser, { loading }] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\", // Return both data and errors\n});\n\nconst { data, errors } = await createUser({\n  variables: { input },\n});\n\n// Handle partial success\nif (data?.createUser) {\n  console.log(\"User created:\", data.createUser);\n}\nif (errors) {\n  console.warn(\"Some errors occurred:\", errors);\n}\n```\n\n### onError Callback\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  onError: (error) => {\n    // Handle error globally\n    toast.error(`Failed to create user: ${error.message}`);\n\n    // Log to error tracking service\n    Sentry.captureException(error);\n  },\n  onCompleted: (data) => {\n    toast.success(`User ${data.createUser.name} created!`);\n  },\n});\n```\n\n### Field-Level Errors\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\",\n});\n\nconst handleSubmit = async (input: CreateUserInput) => {\n  const { data, errors } = await createUser({\n    variables: { input },\n  });\n\n  // Handle GraphQL validation errors\n  const fieldErrors = errors?.reduce(\n    (acc, error) => {\n      const field = error.extensions?.field as string;\n      if (field) {\n        acc[field] = error.message;\n      }\n      return acc;\n    },\n    {} as Record<string, string>\n  );\n\n  if (fieldErrors?.email) {\n    setEmailError(fieldErrors.email);\n  }\n};\n```\n","isInternal":false,"tokens":3001,"sizeBytes":12073},{"name":"queries.md","path":"docs/agent-skills/apollo-client/references/queries.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/queries.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Queries Reference\n\n> **Note**: In most applications, there should only be one use of a query hook per page. Use fragment-reading hooks (`useFragment`, `useSuspenseFragment`) with component-colocated fragments and data masking for the rest of the page components.\n\n## Table of Contents\n\n- [useQuery Hook](#usequery-hook)\n- [Query Variables](#query-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [useLazyQuery](#uselazyquery)\n- [Polling and Refetching](#polling-and-refetching)\n- [Fetch Policies](#fetch-policies)\n- [Conditional Queries](#conditional-queries)\n\n## useQuery Hook\n\nThe `useQuery` hook is the primary way to fetch data in Apollo Client in non-suspenseful applications. It returns loading and error states that must be handled.\n\n> **Note**: In suspenseful applications, use `useSuspenseQuery` or `useBackgroundQuery` instead. See the [Suspense Hooks reference](suspense-hooks.md) for more details.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_DOGS = gql`\n  query GetDogs {\n    dogs {\n      id\n      breed\n      displayImage\n    }\n  }\n`;\n\nfunction Dogs() {\n  const { loading, error, data } = useQuery(GET_DOGS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  loading, // True during initial load\n  error, // ApolloError if request failed\n  networkStatus, // Detailed network state (1-8)\n  dataState, // For TypeScript type narrowing (AC 4.x)\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n  startPolling, // Start polling at interval\n  stopPolling, // Stop polling\n  subscribeToMore, // Add subscription to query\n  updateQuery, // Manually update query result\n  client, // Apollo Client instance\n  called, // True if query has been executed\n  previousData, // Previous data (useful during loading)\n} = useQuery(QUERY);\n```\n\n## Query Variables\n\n### Basic Variables\n\n```tsx\nconst GET_DOG = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DogPhoto({ breed }: { breed: string }) {\n  const { loading, error, data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  if (loading) return null;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <img src={data.dog.displayImage} alt={breed} />;\n}\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters for better type safety:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetDogData {\n  dog: {\n    id: string;\n    displayImage: string;\n  };\n}\n\ninterface GetDogVariables {\n  breed: string;\n}\n\nconst GET_DOG: TypedDocumentNode<GetDogData, GetDogVariables> = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_DOG, {\n  variables: { breed: \"bulldog\" },\n});\n\n// data?.dog is fully typed\n```\n\n### Dynamic Variables\n\n```tsx\nfunction DogSelector() {\n  const [breed, setBreed] = useState(\"bulldog\");\n\n  // Query automatically re-runs when breed changes\n  const { data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  return (\n    <select value={breed} onChange={(e) => setBreed(e.target.value)}>\n      <option value=\"bulldog\">Bulldog</option>\n      <option value=\"poodle\">Poodle</option>\n    </select>\n  );\n}\n```\n\n## Loading and Error States\n\n### Using Previous Data\n\n```tsx\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, data, previousData } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // Show previous data while loading new data\n  const displayData = data ?? previousData;\n\n  return (\n    <div>\n      {loading && <LoadingSpinner />}\n      {displayData && <UserCard user={displayData.user} />}\n    </div>\n  );\n}\n```\n\n### Network Status\n\n```tsx\nimport { NetworkStatus } from \"@apollo/client\";\n\nfunction Dogs() {\n  const { loading, error, data, networkStatus, refetch } = useQuery(GET_DOGS, {\n    notifyOnNetworkStatusChange: true,\n  });\n\n  if (networkStatus === NetworkStatus.refetch) {\n    return <p>Refetching...</p>;\n  }\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <>\n      <button onClick={() => refetch()}>Refresh</button>\n      <ul>\n        {data.dogs.map((dog) => (\n          <li key={dog.id}>{dog.breed}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n## useLazyQuery\n\nUse `useLazyQuery` when you want to execute a query in response to a user-triggered event (like a button click) rather than on component mount.\n\n**Important**: `useLazyQuery` doesn't guarantee a network request - it only sets variables. If data is already in the cache, this isn't a \"refetch\". Only use `useLazyQuery` if you consume the second tuple value (loading, data, error states) to synchronize cache data with the component. If you only need the promise, use `client.query` directly instead.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useLazyQuery } from \"@apollo/client/react\";\n\nconst GET_DOG_PHOTO = gql`\n  query GetDogPhoto($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DelayedQuery() {\n  const [getDog, { loading, error, data, called }] =\n    useLazyQuery(GET_DOG_PHOTO);\n\n  if (called && loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <div>\n      {data?.dog && <img src={data.dog.displayImage} />}\n      <button onClick={() => getDog({ variables: { breed: \"bulldog\" } })}>\n        Get Bulldog Photo\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use client.query Instead\n\nIf you only need the promise result and don't consume the loading/error/data states from the hook, use `client.query` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction SearchDogs() {\n  const client = useApolloClient();\n  const [search, setSearch] = useState(\"\");\n\n  const handleSearch = async () => {\n    try {\n      const { data } = await client.query({\n        query: SEARCH_DOGS,\n        variables: { query: search },\n      });\n      console.log(\"Found dogs:\", data.searchDogs);\n    } catch (error) {\n      console.error(\"Search failed:\", error);\n    }\n  };\n\n  return (\n    <div>\n      <input value={search} onChange={(e) => setSearch(e.target.value)} />\n      <button onClick={handleSearch}>Search</button>\n    </div>\n  );\n}\n```\n\n## Polling and Refetching\n\n### Polling\n\n```tsx\nfunction LiveFeed() {\n  const { data, startPolling, stopPolling } = useQuery(GET_FEED, {\n    pollInterval: 5000, // Poll every 5 seconds\n  });\n\n  // Or control polling dynamically\n  useEffect(() => {\n    startPolling(5000);\n    return () => stopPolling();\n  }, [startPolling, stopPolling]);\n\n  return <Feed items={data?.feed} />;\n}\n```\n\n### Manual Refetching\n\n```tsx\nfunction DogList() {\n  const { data, refetch } = useQuery(GET_DOGS);\n\n  return (\n    <div>\n      <button onClick={() => refetch()}>Refresh</button>\n      <button onClick={() => refetch({ breed: \"poodle\" })}>\n        Refetch Poodles\n      </button>\n      <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>\n    </div>\n  );\n}\n```\n\n## Fetch Policies\n\nControl how the query interacts with the cache.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-only`        | Only return cached data, never fetch                       |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n| `standby`           | Same as cache-first but doesn't auto-update                |\n\n### Usage Examples\n\n```tsx\n// Real-time data - always fetch\nconst { data } = useQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Static data - prefer cache\nconst { data } = useQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data, loading } = useQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n\n// Fetch once, then use cache\nconst { data } = useQuery(GET_USER_PROFILE, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n```\n\n### nextFetchPolicy\n\n```tsx\n// First request: network-only\n// Subsequent requests: cache-first\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n\n// Or use a function for more control\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: (currentFetchPolicy, { reason, observable }) => {\n    if (reason === \"after-fetch\") {\n      return \"cache-first\";\n    }\n    return currentFetchPolicy;\n  },\n});\n```\n\n## Conditional Queries\n\n### Using skipToken (Recommended)\n\nUse `skipToken` to conditionally skip queries without TypeScript issues:\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n### Skip Option (Alternative)\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId! },\n    skip: !userId, // Don't execute if no userId\n  });\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n> **Note**: Using `skipToken` is preferred over `skip` as it avoids TypeScript issues with required variables and the non-null assertion operator.\n\n### SSR Skip\n\n```tsx\n// Skip during server-side rendering\nconst { data } = useQuery(GET_USER_LOCATION, {\n  skip: typeof window === \"undefined\",\n  ssr: false,\n});\n```\n","isInternal":false,"tokens":2641,"sizeBytes":10283},{"name":"state-management.md","path":"docs/agent-skills/apollo-client/references/state-management.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/state-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# State Management Reference\n\n## Table of Contents\n\n- [Reactive Variables](#reactive-variables)\n- [Local-Only Fields](#local-only-fields)\n- [LocalState Resolvers](#localstate-resolvers)\n- [Combining Remote and Local State](#combining-remote-and-local-state)\n- [useReactiveVar Hook](#usereactivevar-hook)\n\n## Reactive Variables\n\nReactive variables are a way to store local state outside of the Apollo Client cache while still triggering reactive updates.\n\n**Important**: Reactive variables store a single value that notifies `ApolloClient` instances when changed. They do not have separate values per ApolloClient instance. In multi-user environments like SSR, global or module-level reactive variables could be shared between users and cause data leaks. In frameworks that use SSR, always avoid storing reactive variables as globals.\n\n### Creating Reactive Variables\n\n```typescript\nimport { makeVar } from \"@apollo/client\";\n\n// Simple reactive variable\nexport const isLoggedInVar = makeVar<boolean>(false);\n\n// Object reactive variable\nexport const cartItemsVar = makeVar<CartItem[]>([]);\n\n// Complex state\ninterface AppState {\n  theme: \"light\" | \"dark\";\n  sidebarOpen: boolean;\n  notifications: Notification[];\n}\n\nexport const appStateVar = makeVar<AppState>({\n  theme: \"light\",\n  sidebarOpen: true,\n  notifications: [],\n});\n```\n\n### Reading Reactive Variables\n\n```tsx\n// Direct read (non-reactive)\nconst isLoggedIn = isLoggedInVar();\n\n// Reactive read in component\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction AuthButton() {\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return isLoggedIn ?\n      <button onClick={() => isLoggedInVar(false)}>Logout</button>\n    : <button onClick={() => isLoggedInVar(true)}>Login</button>;\n}\n```\n\n### Updating Reactive Variables\n\n```typescript\n// Set new value\nisLoggedInVar(true);\n\n// Update based on current value\ncartItemsVar([...cartItemsVar(), newItem]);\n\n// Update object state\nappStateVar({\n  ...appStateVar(),\n  theme: \"dark\",\n});\n\n// Helper function pattern\nexport function toggleSidebar() {\n  const current = appStateVar();\n  appStateVar({ ...current, sidebarOpen: !current.sidebarOpen });\n}\n\nexport function addNotification(notification: Notification) {\n  const current = appStateVar();\n  appStateVar({\n    ...current,\n    notifications: [...current.notifications, notification],\n  });\n}\n```\n\n## Local-Only Fields\n\nLocal-only fields are fields defined in queries but resolved entirely on the client using the `@client` directive.\n\n**Important**: To use any `@client` fields, you need to add `LocalState` to the `ApolloClient` initialization:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({}),\n  // ... other options\n});\n```\n\n> **Note**: `LocalState` is an Apollo Client 4.x concept and did not exist as a class in previous versions. In previous versions, a `localState` option was not necessary, and local resolvers (if used) could be passed directly to the `ApolloClient` constructor.\n\n### Basic @client Fields\n\n```tsx\nconst GET_USER_WITH_LOCAL = gql`\n  query GetUserWithLocal($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n      # Local-only fields\n      isSelected @client\n      displayName @client\n    }\n  }\n`;\n\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER_WITH_LOCAL, {\n    variables: { id: userId },\n  });\n\n  return (\n    <div className={data?.user.isSelected ? \"selected\" : \"\"}>\n      <h2>{data?.user.displayName}</h2>\n      <p>{data?.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Local Field Read Functions (Type Policies)\n\nLocal field `read` functions are defined in entity-level type policies. You can use reactive variables inside these `read` functions, along with other calculations or derived values:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Simple local field from reactive variable\n        isSelected: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return selectedUsersVar().includes(id);\n          },\n        },\n\n        // Computed local field (derived value)\n        displayName: {\n          read(_, { readField }) {\n            const name = readField(\"name\");\n            const email = readField(\"email\");\n            return name || email?.split(\"@\")[0] || \"Anonymous\";\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## LocalState Resolvers\n\n### Query-Level Local Resolvers\n\nQuery-level local fields can be defined using `LocalState` resolvers. **Note**: Do not read reactive variables inside LocalState resolvers - this is not a documented/tested feature. It might not behave as expected.\n\n```typescript\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Query: {\n        // Read from localStorage\n        theme: () => {\n          if (typeof window !== \"undefined\") {\n            return localStorage.getItem(\"theme\") || \"light\";\n          }\n          return \"light\";\n        },\n\n        // Read from cache\n        currentUser: (_, __, { cache }) => {\n          if (typeof window === \"undefined\") return null;\n          const userId = localStorage.getItem(\"currentUserId\");\n          if (!userId) return null;\n          return cache.readFragment({\n            id: cache.identify({ __typename: \"User\", id: userId }),\n            fragment: gql`\n              fragment CurrentUser on User {\n                id\n                name\n                email\n              }\n            `,\n          });\n        },\n\n        // Compute value\n        isOnline: () => {\n          if (typeof navigator !== \"undefined\") {\n            return navigator.onLine;\n          }\n          return true;\n        },\n      },\n    },\n  }),\n});\n```\n\n### Using Local Query Fields\n\n```tsx\nconst GET_AUTH_STATE = gql`\n  query GetAuthState {\n    isLoggedIn @client\n    currentUser @client {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction AuthStatus() {\n  const { data } = useQuery(GET_AUTH_STATE);\n\n  if (!data?.isLoggedIn) {\n    return <LoginButton />;\n  }\n\n  return <UserMenu user={data.currentUser} />;\n}\n```\n\n## Combining Remote and Local State\n\n### Mixing Remote and Local Fields\n\n```tsx\nconst GET_PRODUCTS = gql`\n  query GetProducts {\n    products {\n      id\n      name\n      price\n      # Local fields\n      quantity @client\n      isInCart @client\n    }\n  }\n`;\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Product: {\n      fields: {\n        quantity: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            const cartItem = cartItemsVar().find(\n              (item) => item.productId === id\n            );\n            return cartItem?.quantity ?? 0;\n          },\n        },\n\n        isInCart: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return cartItemsVar().some((item) => item.productId === id);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Local Mutations\n\n```tsx\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Mutation: {\n        addToCart: (_, { productId, quantity }, { cache }) => {\n          // Read current cart from cache\n          const { cart } = cache.readQuery({ query: GET_CART }) || { cart: [] };\n\n          const existing = cart.find((item) => item.productId === productId);\n\n          const updatedCart =\n            existing ?\n              cart.map((item) =>\n                item.productId === productId ?\n                  { ...item, quantity: item.quantity + quantity }\n                : item\n              )\n            : [...cart, { productId, quantity, __typename: \"CartItem\" }];\n\n          // Write updated cart back to cache\n          cache.writeQuery({\n            query: GET_CART,\n            data: { cart: updatedCart },\n          });\n\n          return true;\n        },\n      },\n    },\n  }),\n});\n\nconst ADD_TO_CART = gql`\n  mutation AddToCart($productId: ID!, $quantity: Int!) {\n    addToCart(productId: $productId, quantity: $quantity) @client\n  }\n`;\n```\n\n### Persisting Local State\n\n```typescript\n// Create a helper function to permanently subscribe to reactive variable changes, without creating memory leaks\nfunction subscribeToVariable<T>(\n  weakRef: WeakRef<ReactiveVar<T>>,\n  listener: ReactiveListener<T>\n) {\n  weakRef.deref()?.onNextChange((value) => {\n    listener(value);\n    subscribeToVariable(weakRef, listener);\n  });\n}\n\n// Create reactive variable with persistence\nconst persistentCartVar = makeVar<CartItem[]>(\n  typeof window !== \"undefined\" && localStorage.getItem(\"cart\") ?\n    JSON.parse(localStorage.getItem(\"cart\")!)\n  : []\n);\n\n// Save to localStorage when reactive variable changes\nsubscribeToVariable(new WeakRef(persistentCartVar), (items) => {\n  try {\n    if (typeof window !== \"undefined\") {\n      localStorage.setItem(\"cart\", JSON.stringify(items));\n    }\n  } catch (error) {\n    console.error(\"Failed to persist cart:\", error);\n  }\n});\n```\n\n## useReactiveVar Hook\n\nThe `useReactiveVar` hook subscribes a component to reactive variable updates.\n\n### Basic Usage\n\n```tsx\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction ThemeToggle() {\n  const theme = useReactiveVar(themeVar);\n\n  return (\n    <button onClick={() => themeVar(theme === \"light\" ? \"dark\" : \"light\")}>\n      Current: {theme}\n    </button>\n  );\n}\n```\n\n### With Derived State\n\n```tsx\nfunction CartSummary() {\n  const cartItems = useReactiveVar(cartItemsVar);\n\n  // Derived values are computed on each render\n  const totalItems = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n  const totalPrice = cartItems.reduce(\n    (sum, item) => sum + item.price * item.quantity,\n    0\n  );\n\n  return (\n    <div>\n      <p>Items: {totalItems}</p>\n      <p>Total: ${totalPrice.toFixed(2)}</p>\n    </div>\n  );\n}\n```\n\n### Multiple Reactive Variables\n\n```tsx\nfunction AppLayout() {\n  const theme = useReactiveVar(themeVar);\n  const sidebarOpen = useReactiveVar(sidebarOpenVar);\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return (\n    <div className={`app ${theme}`}>\n      {isLoggedIn && sidebarOpen && <Sidebar />}\n      <main>\n        <Outlet />\n      </main>\n    </div>\n  );\n}\n```\n","isInternal":false,"tokens":2379,"sizeBytes":10515},{"name":"suspense-hooks.md","path":"docs/agent-skills/apollo-client/references/suspense-hooks.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/suspense-hooks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Suspense Hooks Reference\n\n> **Note**: Suspense hooks are the recommended approach for data fetching in modern React applications (React 18+). They provide cleaner code, better loading state handling, and enable streaming SSR.\n\n## Table of Contents\n\n- [useSuspenseQuery Hook](#usesuspensequery-hook)\n- [useBackgroundQuery and useReadQuery](#usebackgroundquery-and-usereadquery)\n- [useLoadableQuery](#useloadablequery)\n- [createQueryPreloader](#createquerypreloader)\n- [useQueryRefHandlers](#usequeryrefhandlers)\n- [Distinguishing Queries with queryKey](#distinguishing-queries-with-querykey)\n- [Suspense Boundaries and Error Handling](#suspense-boundaries-and-error-handling)\n- [Transitions](#transitions)\n- [Avoiding Request Waterfalls](#avoiding-request-waterfalls)\n- [Fetch Policies](#fetch-policies)\n- [Streaming SSR or React Server Components](#streaming-ssr-or-react-server-components)\n- [Conditional Queries](#conditional-queries)\n\n## useSuspenseQuery Hook\n\nThe `useSuspenseQuery` hook is the Suspense-ready replacement for `useQuery`. It initiates a network request and causes the component calling it to suspend while the request is made. Unlike `useQuery`, it does not return `loading` states—these are handled by React's Suspense boundaries and error boundaries.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { GET_DOG } from \"./queries.generated\";\n\nfunction App() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Dog id=\"3\" />\n    </Suspense>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  // data is always defined when this component renders\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  dataState, // With default options: \"complete\" | \"streaming\"\n  // With returnPartialData: also \"partial\"\n  // With errorPolicy \"all\" or \"ignore\": also \"empty\"\n  error, // ApolloError (only when errorPolicy is \"all\" or \"ignore\")\n  networkStatus, // NetworkStatus.ready, NetworkStatus.loading, etc.\n  client, // Apollo Client instance\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n} = useSuspenseQuery(QUERY, options);\n```\n\n### Key Differences from useQuery\n\n- **No `loading` boolean**: Component suspends instead of returning `loading: true`\n- **Error handling**: With default `errorPolicy` (`none`), errors are thrown and caught by error boundaries. With `errorPolicy: \"all\"` or `\"ignore\"`, the `error` property is returned and `data` may be `undefined`.\n- **`data` availability**: With default `errorPolicy` (`none`), `data` is guaranteed to be present when the component renders. With `errorPolicy: \"all\"` or `\"ignore\"`, when `dataState` is `empty`, `data` may be `undefined`.\n- **Suspense boundaries**: Must wrap component with `<Suspense>` to handle loading state\n\n### Changing Variables\n\nWhen variables change, `useSuspenseQuery` automatically re-runs the query. If the data is not in the cache, the component suspends again.\n\n```tsx\nimport { useState } from \"react\";\nimport { GET_DOGS } from \"./queries.generated\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => setSelectedDog(e.target.value)}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog.name}</div>\n      <div>Breed: {data.dog.breed}</div>\n    </>\n  );\n}\n```\n\n### Rendering Partial Data\n\nUse `returnPartialData` to render immediately with partial cache data instead of suspending. The component will still suspend if there is no data in the cache.\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    returnPartialData: true,\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog?.name ?? \"Unknown\"}</div>\n      {data.dog?.breed && <div>Breed: {data.dog.breed}</div>}\n    </>\n  );\n}\n```\n\n## useBackgroundQuery and useReadQuery\n\nUse `useBackgroundQuery` with `useReadQuery` to avoid request waterfalls by starting a query in a parent component and reading the result in a child component. This pattern enables the parent to start fetching data before the child component renders.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useBackgroundQuery, useReadQuery } from \"@apollo/client/react\";\n\nfunction Parent() {\n  // Start fetching immediately\n  const [queryRef] = useBackgroundQuery(GET_DOG, {\n    variables: { id: \"3\" },\n  });\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Child queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction Child({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  // Read the query result\n  const { data } = useReadQuery(queryRef);\n\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### When to Use\n\n- **Avoiding waterfalls**: Start fetching data in a parent (preferably above a suspense boundary) before child components render\n- **Preloading data**: Begin fetching before the component that needs the data is ready\n- **Parallel queries**: Start multiple queries at once in a parent component\n\n### Return Values\n\n`useBackgroundQuery` returns a tuple:\n\n```typescript\nconst [\n  queryRef, // QueryRef to pass to useReadQuery\n  { refetch, fetchMore, subscribeToMore }, // Helper functions\n] = useBackgroundQuery(QUERY, options);\n```\n\n`useReadQuery` returns the query result:\n\n```typescript\nconst {\n  data, // Query result data (always defined)\n  dataState, // \"complete\" | \"streaming\" | \"partial\" | \"empty\"\n  error, // ApolloError (if errorPolicy allows)\n  networkStatus, // Detailed network state (1-8)\n} = useReadQuery(queryRef);\n```\n\n## useLoadableQuery\n\nUse `useLoadableQuery` to imperatively load a query in response to a user interaction (like a button click) instead of on component mount.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useLoadableQuery, useReadQuery } from \"@apollo/client/react\";\nimport { GET_GREETING } from \"./queries.generated\";\n\nfunction App() {\n  const [loadGreeting, queryRef] = useLoadableQuery(GET_GREETING);\n\n  return (\n    <>\n      <button\n        onClick={() => loadGreeting({ variables: { language: \"english\" } })}\n      >\n        Load Greeting\n      </button>\n      <Suspense fallback={<div>Loading...</div>}>\n        {queryRef && <Greeting queryRef={queryRef} />}\n      </Suspense>\n    </>\n  );\n}\n\nfunction Greeting({ queryRef }: { queryRef: QueryRef<GreetingData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.greeting.message}</div>;\n}\n```\n\n### Return Values\n\n```typescript\nconst [\n  loadQuery, // Function to load the query\n  queryRef, // QueryRef (null until loadQuery is called)\n  { refetch, fetchMore, subscribeToMore, reset }, // Helper functions\n] = useLoadableQuery(QUERY, options);\n```\n\n### When to Use\n\n- **User-triggered fetching**: Load data in response to user actions\n- **Lazy loading**: Defer data fetching until it's actually needed\n- **Progressive disclosure**: Load data for UI elements that may not be initially visible\n\n## createQueryPreloader\n\nThe `createQueryPreloader` function creates a `preloadQuery` function that can be used to initiate queries outside of React components. This is useful for preloading data before a component renders, such as in route loaders or event handlers.\n\n### Basic Usage\n\n```tsx\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createQueryPreloader } from \"@apollo/client/react\";\n\nconst client = new ApolloClient({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  cache: new InMemoryCache(),\n});\n\n// Create a preload function\nexport const preloadQuery = createQueryPreloader(client);\n```\n\n### Using preloadQuery with Route Loaders\n\n> **Note**: This example applies to React Router in non-framework mode. For React Router framework mode, see [integration-react-router.md](./integration-react-router.md).\n\nUse the preload function with React Router's `loader` function to begin loading data during route transitions:\n\n```tsx\nimport { preloadQuery } from \"@/lib/apollo-client\";\nimport { GET_DOG } from \"./queries.generated\";\n\n// React Router loader function\nexport async function loader({ params }: { params: { id: string } }) {\n  return preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n}\n\n// Route component\nexport default function DogRoute() {\n  const queryRef = useLoaderData();\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <DogDetails queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction DogDetails({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.dog.name}</h1>\n      <p>Breed: {data.dog.breed}</p>\n    </div>\n  );\n}\n```\n\n### Preventing Route Transitions Until Query Loads\n\nUse `preloadQuery.toPromise(queryRef)` to prevent route transitions until the query finishes loading:\n\n```tsx\nexport async function loader({ params }: { params: { id: string } }) {\n  const queryRef = preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n\n  // Wait for the query to complete before transitioning\n  return preloadQuery.toPromise(queryRef);\n}\n```\n\nWhen `preloadQuery.toPromise()` is used, the route transition waits for the query to complete, and the data renders immediately without showing a loading fallback.\n\n> **Note**: `preloadQuery.toPromise()` resolves with the `queryRef` itself (not the data) to encourage using `useReadQuery` for cache updates. If you need raw query data in your loader, use `client.query()` directly.\n\n### With Next.js Server Components\n\n> **Note**: For Next.js App Router, use the `PreloadQuery` component from `@apollo/client-integration-nextjs` instead. See [integration-nextjs.md](./integration-nextjs.md) for details.\n\n## useQueryRefHandlers\n\nThe `useQueryRefHandlers` hook provides access to `refetch` and `fetchMore` functions for queries initiated with `preloadQuery`, `useBackgroundQuery`, or `useLoadableQuery`. This is useful when you need to refetch or paginate data in components where the `queryRef` is passed through.\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n### Basic Usage\n\n```tsx\nimport { useQueryRefHandlers } from \"@apollo/client/react\";\n\nfunction Breeds({ queryRef }: { queryRef: QueryRef<BreedsData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            refetch();\n          });\n        }}\n      >\n        {isPending ? \"Refetching...\" : \"Refetch breeds\"}\n      </button>\n      <ul>\n        {data.breeds.map((breed) => (\n          <li key={breed.id}>{breed.name}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### With Pagination\n\nUse `fetchMore` to implement pagination:\n\n```tsx\nfunction Posts({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { fetchMore } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            fetchMore({\n              variables: {\n                offset: data.posts.length,\n              },\n            });\n          });\n        }}\n      >\n        {isPending ? \"Loading...\" : \"Load more\"}\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Preloaded queries**: Access refetch/fetchMore for queries initiated with `preloadQuery`\n- **Background queries**: Use in child components receiving `queryRef` from `useBackgroundQuery`\n- **Loadable queries**: Refetch or paginate queries initiated with `useLoadableQuery`\n- **React transitions**: Integrate with transitions to avoid showing loading fallbacks during refetches\n\n## Distinguishing Queries with queryKey\n\nApollo Client uses the combination of `query` and `variables` to uniquely identify each query. When multiple components use the same `query` and `variables`, they share the same identity and suspend at the same time, regardless of which component initiates the request.\n\nUse the `queryKey` option to ensure each hook has a unique identity:\n\n```tsx\nfunction UserProfile() {\n  // First query with unique key\n  const { data: userData } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-profile\"],\n  });\n\n  // Second query with same query and variables but different key\n  const { data: userPreview } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-preview\"],\n  });\n\n  return (\n    <div>\n      <UserCard user={userData.user} />\n      <UserSidebar user={userPreview.user} />\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Multiple instances**: When rendering multiple components that use the same query and variables\n- **Preventing shared suspension**: When you want independent control over when each query suspends\n- **Separate cache entries**: When you need to maintain separate cache states for the same query\n\n> **Note**: Each item in the `queryKey` array must be a stable identifier to prevent infinite fetches.\n\n## Suspense Boundaries and Error Handling\n\n### Suspense Boundaries\n\nWrap components that use Suspense hooks with `<Suspense>` boundaries to handle loading states. Place boundaries strategically to control the granularity of loading indicators.\n\n```tsx\nfunction App() {\n  return (\n    <>\n      {/* Top-level loading for entire page */}\n      <Suspense fallback={<PageSpinner />}>\n        <Header />\n        <Content />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Content() {\n  return (\n    <>\n      <MainSection />\n      {/* Granular loading for sidebar */}\n      <Suspense fallback={<SidebarSkeleton />}>\n        <Sidebar />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Error Boundaries\n\nSuspense hooks throw errors to React error boundaries instead of returning them. Use error boundaries to handle GraphQL errors.\n\n```tsx\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nfunction App() {\n  return (\n    <ErrorBoundary\n      fallback={({ error }) => (\n        <div>\n          <h2>Something went wrong</h2>\n          <p>{error.message}</p>\n        </div>\n      )}\n    >\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id=\"3\" />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n```\n\n### Custom Error Policies\n\nUse `errorPolicy` to control how errors are handled:\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data, error } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    errorPolicy: \"all\", // Return both data and errors\n  });\n\n  return (\n    <>\n      <div>Name: {data?.dog?.name ?? \"Unknown\"}</div>\n      {error && <div>Warning: {error.message}</div>}\n    </>\n  );\n}\n```\n\n## Transitions\n\nUse React transitions to avoid showing loading UI when updating state. Transitions keep the previous UI visible while new data is fetching.\n\n### Using startTransition\n\n```tsx\nimport { useState, Suspense, startTransition } from \"react\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => {\n          // Wrap state update in startTransition\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Using useTransition\n\nUse `useTransition` to get an `isPending` flag for visual feedback during transitions.\n\n```tsx\nimport { useState, Suspense, useTransition } from \"react\";\n\nfunction DogSelector() {\n  const [isPending, startTransition] = useTransition();\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        style={{ opacity: isPending ? 0.5 : 1 }}\n        value={selectedDog}\n        onChange={(e) => {\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n## Avoiding Request Waterfalls\n\nRequest waterfalls occur when a child component waits for the parent to finish rendering before it can start fetching its own data. Use `useBackgroundQuery` to start fetching child data earlier in the component tree.\n\n> **Note**: When one query depends on the result of another query (e.g., the child query needs an ID from the parent query), the waterfall is unavoidable. The best solution is to restructure your schema to fetch all needed data in a single nested query.\n\n### Example: Independent Queries\n\nWhen queries don't depend on each other, use `useBackgroundQuery` to start them in parallel:\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: String!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nconst GET_POSTS = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n    }\n  }\n`;\n\nfunction Parent() {\n  // Both queries start immediately - no waterfall\n  const [userRef] = useBackgroundQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const [postsRef] = useBackgroundQuery(GET_POSTS);\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserProfile queryRef={userRef} />\n      <PostsList queryRef={postsRef} />\n    </Suspense>\n  );\n}\n\nfunction UserProfile({ queryRef }: { queryRef: QueryRef<UserData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>User: {data.user.name}</div>;\n}\n\nfunction PostsList({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <ul>\n      {data.posts.map((post) => (\n        <li key={post.id}>{post.title}</li>\n      ))}\n    </ul>\n  );\n}\n```\n\n## Fetch Policies\n\nSuspense hooks support most of the same fetch policies as `useQuery`, controlling how the query interacts with the cache. Note that `cache-only` and `standby` are not supported by Suspense hooks.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n\n### Usage Examples\n\n```tsx\n// Always fetch fresh data\nconst { data } = useSuspenseQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Prefer cached data\nconst { data } = useSuspenseQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data } = useSuspenseQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n```\n\n## Streaming SSR or React Server Components\n\nApollo Client integrates with modern React frameworks that support Streaming SSR and React Server Components. For detailed setup instructions specific to your framework, see:\n\n- **Next.js App Router**: [integration-nextjs.md](./integration-nextjs.md) - Includes React Server Components, PreloadQuery component, and streaming SSR\n- **React Router**: [integration-react-router.md](./integration-react-router.md) - Framework mode with SSR support\n- **TanStack Start**: [integration-tanstack-start.md](./integration-tanstack-start.md) - Full-stack React framework with SSR\n\nThese guides cover:\n\n- Framework-specific client setup and configuration\n- Preloading queries for optimal performance\n- Streaming SSR with `useBackgroundQuery` and Suspense\n- Error handling in server-rendered environments\n\n## Conditional Queries\n\n### Using skipToken\n\nUse `skipToken` to conditionally skip queries without TypeScript issues. When `skipToken` is used, the component won't suspend and `data` will be `undefined`.\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data, dataState } = useSuspenseQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  if (dataState !== \"complete\") {\n    return <p>Select a user</p>;\n  }\n\n  return <Profile user={data.user} />;\n}\n```\n\n### Conditional Rendering\n\nAlternatively, use conditional rendering to control when Suspense hooks are called. This provides better type safety and clearer component logic.\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  if (!userId) {\n    return <p>Select a user</p>;\n  }\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserDetails userId={userId} />\n    </Suspense>\n  );\n}\n\nfunction UserDetails({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <Profile user={data.user} />;\n}\n```\n\n> **Note**: Using conditional rendering with `skipToken` provides better type safety and avoids issues with required variables. The `skip` option is deprecated in favor of `skipToken`.\n","isInternal":false,"tokens":5390,"sizeBytes":22494},{"name":"troubleshooting.md","path":"docs/agent-skills/apollo-client/references/troubleshooting.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/troubleshooting.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Troubleshooting Reference\n\n## Table of Contents\n\n- [Setup Issues](#setup-issues)\n- [Cache Issues](#cache-issues)\n- [TypeScript Issues](#typescript-issues)\n- [Performance Issues](#performance-issues)\n- [DevTools Usage](#devtools-usage)\n- [Common Error Messages](#common-error-messages)\n\n## Setup Issues\n\n### Provider Not Found\n\n**Error:** `Could not find \"client\" in the context or passed in as an option`\n\n**Cause:** Component is not wrapped with `ApolloProvider`.\n\n**Solution:**\n\n```tsx\n// Ensure ApolloProvider wraps your app\nimport { ApolloProvider } from \"@apollo/client\";\n\nfunction App() {\n  return (\n    <ApolloProvider client={client}>\n      <YourApp />\n    </ApolloProvider>\n  );\n}\n```\n\n### Multiple Apollo Clients\n\n**Problem:** Unintended cache isolation or conflicting states.\n\n**Solution:** Use a single client instance or explicitly manage multiple clients:\n\n```tsx\n// Single client (recommended)\nconst client = new ApolloClient({\n  /* ... */\n});\n\nexport function App() {\n  return (\n    <ApolloProvider client={client}>\n      <Router />\n    </ApolloProvider>\n  );\n}\n\n// Multiple clients (rare use case)\nconst publicClient = new ApolloClient({\n  uri: \"/public/graphql\",\n  cache: new InMemoryCache(),\n});\nconst adminClient = new ApolloClient({\n  uri: \"/admin/graphql\",\n  cache: new InMemoryCache(),\n});\n\nfunction AdminSection() {\n  return (\n    <ApolloProvider client={adminClient}>\n      <AdminDashboard />\n    </ApolloProvider>\n  );\n}\n```\n\n### Client Created in Component\n\n**Problem:** New client on every render causes cache loss.\n\n**Solution:** Create client outside component or use a ref pattern:\n\n```tsx\n// Bad - new client on every render\nfunction App() {\n  const client = new ApolloClient({\n    /* ... */\n  }); // Don't do this!\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Module-level client definition\n// Okay if there is a 100% guarantee this application will never use SSR\nconst client = new ApolloClient({\n  /* ... */\n});\nfunction App() {\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Good - store Apollo Client in a ref that is initialized once\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    storeRef.current = makeApolloClient();\n  }\n  return storeRef.current;\n}\n\n// Better - singleton global in non-SSR environments to survive unmounts\nconst singleton = Symbol.for(\"ApolloClientSingleton\");\ndeclare global {\n  interface Window {\n    [singleton]?: ApolloClient;\n  }\n}\n\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    if (typeof window === \"undefined\") {\n      storeRef.current = makeApolloClient();\n    } else {\n      window[singleton] ??= makeApolloClient();\n      storeRef.current = window[singleton];\n    }\n  }\n  return storeRef.current;\n}\n// Note: this second option might need manual removal between tests\n```\n\n## Cache Issues\n\n### Stale Data Not Updating\n\n**Problem:** UI doesn't reflect mutations or other updates.\n\n**Solution 1:** Verify cache key identification:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Ensure proper identification\n    Product: {\n      keyFields: [\"id\"], // or ['sku'] if no id field\n    },\n  },\n});\n```\n\n**Solution 2:** Update cache after mutations:\n\n```tsx\nconst [deleteProduct] = useMutation(DELETE_PRODUCT, {\n  update: (cache, { data }) => {\n    cache.evict({ id: cache.identify(data.deleteProduct) });\n    cache.gc();\n  },\n});\n```\n\n**Solution 3:** Use appropriate fetch policy:\n\n```tsx\nconst { data } = useQuery(GET_PRODUCTS, {\n  fetchPolicy: \"cache-and-network\", // Always fetch fresh data\n});\n```\n\n### Missing Cache Updates After Mutation\n\n**Problem:** New items don't appear in lists after creation.\n\n**Solution:** Manually update the cache:\n\n```tsx\nconst [createProduct] = useMutation(CREATE_PRODUCT, {\n  update: (cache, { data }) => {\n    const existing = cache.readQuery<{ products: Product[] }>({\n      query: GET_PRODUCTS,\n    });\n\n    cache.writeQuery({\n      query: GET_PRODUCTS,\n      data: {\n        products: [...(existing?.products ?? []), data.createProduct],\n      },\n    });\n  },\n});\n```\n\n### Pagination Cache Issues\n\n**Problem:** Paginated data not merging correctly.\n\n**Solution:** Configure proper type policies:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        products: {\n          keyArgs: [\"category\"], // Only category creates new cache entries\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Cache Normalization Problems\n\n**Problem:** Objects with same ID showing different data in different queries.\n\n**Debug:** Check cache contents:\n\n```typescript\n// In DevTools console or component\nconsole.log(client.cache.extract());\n```\n\n**Solution:** Ensure consistent `__typename` and `id` fields:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Always include id\n    name\n  }\n}\n```\n\n## TypeScript Issues\n\n### Type Generation Setup\n\n**Problem:** No type safety for GraphQL operations.\n\n**Solution:** Set up GraphQL Code Generator with the [recommended starter configuration](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration), as described in the [Skill](../SKILL.md).\n\n### Using Generated Types\n\n```tsx\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUsersDocument, GetUsersQuery } from \"./generated/graphql\";\n\nfunction UserList() {\n  // Fully typed without manual type annotations\n  const { data, loading, error } = useQuery(GetUsersDocument);\n\n  // data.users is automatically typed as GetUsersQuery['users']\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Performance Issues\n\n### Over-Fetching\n\n**Problem:** Fetching more data than needed.\n\n**Solution:** Select only required fields:\n\n```graphql\n# Bad - fetching everything\nquery GetUsers {\n  users {\n    id\n    name\n    email\n    profile { ... }\n    posts { ... }\n    friends { ... }\n  }\n}\n\n# Good - fetch what's needed\nquery GetUserNames {\n  users {\n    id\n    name\n  }\n}\n```\n\n### N+1 Queries\n\n**Problem:** Multiple network requests for related data.\n\n**Solution:** Structure queries to batch requests. Best practice: use query colocation and compose queries from fragments defined on child components.\n\n```graphql\n# Bad - separate queries\nquery GetUser($id: ID!) {\n  user(id: $id) {\n    id\n    name\n  }\n}\nquery GetUserPosts($userId: ID!) {\n  posts(userId: $userId) {\n    id\n    title\n  }\n}\n\n# Good - single query\nquery GetUserWithPosts($id: ID!) {\n  user(id: $id) {\n    id\n    name\n    posts {\n      id\n      title\n    }\n  }\n}\n```\n\n### Unnecessary Re-Renders\n\n**Problem:** Components re-render when unrelated cache data changes.\n\n**Solution:** Use `useFragment` and data masking for selective field reading. If that is not possible, `useQuery` with `@nonreactive` directives might be an alternative.\n\n```tsx\n// Prefer useFragment with data masking\nconst { data } = useFragment({\n  fragment: USER_FRAGMENT,\n  from: { __typename: \"User\", id },\n});\n\n// Alternative: use @nonreactive directive\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      # This field won't trigger re-renders when it changes\n      metadata @nonreactive {\n        lastSeen\n        preferences\n      }\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_USER, {\n  variables: { id },\n});\n```\n\n### Cache Misses\n\n**Debug:** Use Apollo DevTools to inspect cache.\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  // DevTools are enabled by default in development\n  // Only configure this when you need to enable them in production\n  devtools: {\n    enabled: true,\n  },\n});\n```\n\n## DevTools Usage\n\n### Installing Apollo DevTools\n\nInstall the browser extension:\n\n- [Chrome](https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)\n- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/)\n\n### Enabling DevTools\n\nDevTools are enabled by default in development. Only configure this setting if you need to enable them in production:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  devtools: {\n    enabled: true, // Set to true to enable in production\n  },\n});\n```\n\n### DevTools Features\n\n1. **Cache Inspector**: View normalized cache contents\n2. **Queries**: See active queries and their states\n3. **Mutations**: Track mutation history\n4. **Explorer**: Build and test queries against your schema\n5. **Memoization Limits**: Monitor and track cache memoization\n6. **Cache Writes**: Track all writes to the cache\n\n### Debugging Cache\n\n```typescript\n// Log cache contents\nconsole.log(JSON.stringify(client.cache.extract(), null, 2));\n\n// Check specific object using cache.identify\nconsole.log(\n  client.cache.readFragment({\n    id: cache.identify({ __typename: \"User\", id: 1 }),\n    fragment: gql`\n      fragment _ on User {\n        id\n        name\n        email\n      }\n    `,\n  })\n);\n```\n\n## Common Error Messages\n\n### \"Missing field 'X' in {...}\"\n\n**Cause:** Query doesn't include required field for cache normalization.\n\n**Solution:** Include `id` and `__typename`:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Required for caching\n    __typename # Usually added automatically\n    name\n  }\n}\n```\n\n**Additional advice**: Read the full error message thoroughly.\n\n### \"Store reset while query was in flight\"\n\n**Cause:** `client.resetStore()` called during active queries.\n\n**Solution:** Wait for queries to complete or use `clearStore()`:\n\n```typescript\n// Option 1: Clear without refetching\nawait client.clearStore();\n\n// Option 2: Reset and refetch active queries\nawait client.resetStore();\n```\n\n### \"Invariant Violation: X\"\n\n**Cause:** Various configuration or usage errors.\n\n**Common fixes:**\n\n- Ensure `ApolloProvider` wraps the component tree\n- Check that `gql` tagged templates are valid GraphQL\n- Verify cache configuration matches your schema\n\n### \"Cannot read property 'X' of undefined\"\n\n**Cause:** Accessing data before query completes.\n\n**Solution:** Check `dataState` for proper type narrowing:\n\n```tsx\nconst { data, dataState } = useQuery(GET_USER);\n\n// dataState can be \"complete\", \"partial\", \"streaming\", or \"empty\"\n// It describes the completeness of the data, not a loading state\nif (dataState === \"empty\") return <Spinner />;\n\n// Now data is guaranteed to exist\nreturn <div>{data.user.name}</div>;\n\n// Or use optional chaining\nreturn <div>{data?.user?.name}</div>;\n```\n","isInternal":false,"tokens":2536,"sizeBytes":10750},{"name":"typescript-codegen.md","path":"docs/agent-skills/apollo-client/references/typescript-codegen.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/references/typescript-codegen.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# TypeScript Code Generation\n\nThis guide covers setting up GraphQL Code Generator for type-safe Apollo Client usage with TypeScript.\n\n## Installation\n\n```bash\nnpm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node\n```\n\n## Configuration\n\nCreate a `codegen.ts` file in your project root:\n\n```typescript\n// codegen.ts\nimport { CodegenConfig } from \"@graphql-codegen/cli\";\n\nconst config: CodegenConfig = {\n  overwrite: true,\n  schema: \"<URL_OF_YOUR_GRAPHQL_API>\",\n  // This assumes that all your source files are in a top-level `src/` directory - you might need to adjust this to your file structure\n  documents: [\"src/**/*.{ts,tsx}\"],\n  // Don't exit with non-zero status when there are no documents\n  ignoreNoDocuments: true,\n  generates: {\n    // Use a path that works the best for the structure of your application\n    \"./src/types/__generated__/graphql.ts\": {\n      plugins: [\"typescript\", \"typescript-operations\", \"typed-document-node\"],\n      config: {\n        avoidOptionals: {\n          // Use `null` for nullable fields instead of optionals\n          field: true,\n          // Allow nullable input fields to remain unspecified\n          inputValue: false,\n        },\n        // Use `unknown` instead of `any` for unconfigured scalars\n        defaultScalarType: \"unknown\",\n        // Apollo Client always includes `__typename` fields\n        nonOptionalTypename: true,\n        // Apollo Client doesn't add the `__typename` field to root types so\n        // don't generate a type for the `__typename` for root operation types.\n        skipTypeNameForRoot: true,\n      },\n    },\n  },\n};\n\nexport default config;\n```\n\n## Enable Data Masking\n\nTo enable data masking with GraphQL Code Generator, create a type declaration file to inform Apollo Client about the generated types:\n\n```typescript\n// apollo-client.d.ts\nimport { GraphQLCodegenDataMasking } from \"@apollo/client/masking\";\n\ndeclare module \"@apollo/client\" {\n  export interface TypeOverrides\n    extends GraphQLCodegenDataMasking.TypeOverrides {}\n}\n```\n\n## Running Code Generation\n\nAdd a script to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  }\n}\n```\n\nRun code generation:\n\n```bash\nnpm run codegen\n```\n\n## Usage with Apollo Client\n\nThe typed-document-node plugin generates `TypedDocumentNode` types that Apollo Client hooks automatically infer.\n\n### Defining Operations\n\nDefine your operations inline with the `if (false)` pattern. This allows GraphQL Code Generator to detect and extract operations without executing the code at runtime (bundlers omit this dead code during minification):\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\n// This query will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n```\n\n### Using Generated Types\n\nAfter running `npm run codegen`, import the generated `TypedDocumentNode`:\n\n```typescript\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  // ... other logic ...\n\n  return <div>{data?.user.name}</div>;\n}\n```\n\n## Important Notes\n\n- The typed-document-node plugin might have a bundle size tradeoff but can prevent inconsistencies and is best suited for usage with LLMs, so it is recommended for most applications.\n- See the [GraphQL Code Generator documentation](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration) for other recommended configuration patterns if required.\n- Apollo Client hooks automatically infer types from `TypedDocumentNode` - never use manual generics like `useQuery<QueryType, VariablesType>()`.\n","isInternal":false,"tokens":898,"sizeBytes":4050},{"name":"SKILL.md","path":"docs/agent-skills/apollo-client/SKILL.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/docs/agent-skills/apollo-client/SKILL.md","title":"apollo-client","category":"anthropic-skill","format":"markdown","content":"---\nname: apollo-client\ndescription: >\n  Guide for building React applications with Apollo Client 4.x. Use this skill when:\n  (1) setting up Apollo Client in a React project,\n  (2) writing GraphQL queries or mutations with hooks,\n  (3) configuring caching or cache policies,\n  (4) managing local state with reactive variables,\n  (5) troubleshooting Apollo Client errors or performance issues.\nlicense: MIT\ncompatibility: React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.\nmetadata:\n  author: apollographql\n  version: \"1.0.0\"\nallowed-tools: Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep\n---\n\n# Apollo Client 4.x Guide\n\nApollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Version 4.x brings improved caching, better TypeScript support, and React 19 compatibility.\n\n## Integration Guides\n\nChoose the integration guide that matches your application setup:\n\n- **[Client-Side Apps](references/integration-client.md)** - For client-side React applications without SSR (Vite, Create React App, etc.)\n- **[Next.js App Router](references/integration-nextjs.md)** - For Next.js applications using the App Router with React Server Components\n- **[React Router Framework Mode](references/integration-react-router.md)** - For React Router 7 applications with streaming SSR\n- **[TanStack Start](references/integration-tanstack-start.md)** - For TanStack Start applications with modern routing\n\nEach guide includes installation steps, configuration, and framework-specific patterns optimized for that environment.\n\n## Quick Reference\n\n### Basic Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n### Basic Mutation\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst CREATE_USER = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (name: string) => {\n    await createUser({ variables: { input: { name } } });\n  };\n\n  return <button onClick={() => handleSubmit(\"John\")}>Create User</button>;\n}\n```\n\n### Suspense Query\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<p>Loading user...</p>}>\n      <UserProfile userId=\"1\" />\n    </Suspense>\n  );\n}\n```\n\n## Reference Files\n\nDetailed documentation for specific topics:\n\n- [TypeScript Code Generation](references/typescript-codegen.md) - GraphQL Code Generator setup for type-safe operations\n- [Queries](references/queries.md) - useQuery, useLazyQuery, polling, refetching\n- [Suspense Hooks](references/suspense-hooks.md) - useSuspenseQuery, useBackgroundQuery, useReadQuery, useLoadableQuery\n- [Mutations](references/mutations.md) - useMutation, optimistic UI, cache updates\n- [Fragments](references/fragments.md) - Fragment colocation, useFragment, useSuspenseFragment, data masking\n- [Caching](references/caching.md) - InMemoryCache, typePolicies, cache manipulation\n- [State Management](references/state-management.md) - Reactive variables, local state\n- [Error Handling](references/error-handling.md) - Error policies, error links, retries\n- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions\n\n## Key Rules\n\n### Query Best Practices\n\n- **Each page should generally only have one query, composed from colocated fragments.** Use `useFragment` or `useSuspenseFragment` in all non-page-components. Use `@defer` to allow slow fields below the fold to stream in later and avoid blocking the page load.\n- **Fragments are for colocation, not reuse.** Each fragment should describe exactly the data needs of a specific component, not be shared across components for common fields. See [Fragments reference](references/fragments.md) for details on fragment colocation and data masking.\n- Always handle `loading` and `error` states in UI when using non-suspenseful hooks (`useQuery`, `useLazyQuery`). When using Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n- Use `fetchPolicy` to control cache behavior per query\n- Use the TypeScript type server to look up documentation for functions and options (Apollo Client has extensive docblocks)\n\n### Mutation Best Practices\n\n- **If the schema permits, mutation return values should return everything necessary to update the cache.** Neither manual updates nor refetching should be necessary.\n- If the mutation response is insufficient, carefully weigh manual cache manipulation vs refetching. Manual updates risk missing server logic. Consider optimistic updates with a granular refetch if needed.\n- Handle errors gracefully in the UI\n- Use `refetchQueries` sparingly (prefer letting the cache update automatically)\n\n### Caching Best Practices\n\n- Configure `keyFields` for types without `id` field\n- Disable normalization by setting `keyFields: false` for types that don't include an identifier and are meant to group related fields under the parent\n- Use `typePolicies` for pagination and computed fields\n- Understand cache normalization to debug issues\n- **Enable [data masking](references/fragments.md#data-masking) for all new applications** - it prevents components from accessing fragment data they don't own, enforcing proper data boundaries and preventing over-rendering\n\n### Performance\n\n- Avoid over-fetching with proper field selection\n- Configure appropriate `fetchPolicy` per use case\n- Use `@defer` for incremental delivery of deferred query parts, and `@stream` for streaming list fields (`@stream` available in Apollo Client 4.1+)\n- **Prefer Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications** for better loading state handling and code simplicity\n\n## Ground Rules\n\n- ALWAYS use Apollo Client 4.x patterns (not v3 or earlier)\n- ALWAYS wrap your app with `ApolloProvider`\n- ALWAYS handle loading and error states when using non-suspenseful hooks\n- PREFER Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications for better DX\n- NEVER store Apollo Client in React state (use module-level or context)\n- PREFER `cache-first` for read-heavy data, `network-only` for real-time data\n- USE TypeScript for better type safety with GraphQL\n- IMPLEMENT proper cache updates instead of refetching entire queries\n- ADVISE the human steering you to look into Apollo DevTools when collaboratively debugging Apollo Client issues\n","frontmatter":{"name":"apollo-client","description":"Guide for building React applications with Apollo Client 4.x. Use this skill when: (1) setting up Apollo Client in a React project, (2) writing GraphQL queries or mutations with hooks, (3) configuring caching or cache policies, (4) managing local state with reactive variables, (5) troubleshooting Apollo Client errors or performance issues.\n","license":"MIT","compatibility":"React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.","metadata":{"author":"apollographql","version":"1.0.0"},"allowed-tools":"Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep","allowedTools":["Bash(npm:*)","Bash(npx:*)","Bash(node:*)","Read","Write","Edit","Glob","Grep"]},"allowedTools":["Bash(npm:*)","Bash(npx:*)","Bash(node:*)","Read","Write","Edit","Glob","Grep"],"isInternal":false,"tokens":1670,"sizeBytes":7423},{"name":"chores.md","path":".claude/commands/chores.md","rawUrl":"https://raw.githubusercontent.com/apollographql/apollo-client/HEAD/.claude/commands/chores.md","title":"Command: /chores","category":"command-prompt","format":"markdown","content":"---\nallowed-tools: Bash(git commit:*)\ndescription: Chores\n---\n\n## Context\n\n- Current git status: !`git status`\n- Current git diff (staged and unstaged changes): !`git diff HEAD`\n- Current branch: !`git branch --show-current`\n- Recent commits: !`git log --oneline -10`\n\n## Your task\n\nFetch the latest changes from the remote repository. If the current branch is not up to date, don't do anything, but inform the user about it.\n\nTake a note if the current git status is clean.\n\nRun the npm scripts `format`, `build`, `extract-api:only`, `update-size-limits` after each other in that order.\n\nIf before running the scripts, the git status was clean, create a new commit with the message \"chores\".\n","frontmatter":{"allowed-tools":"Bash(git commit:*)","description":"Chores","allowedTools":["Bash(git","commit:*)"]},"allowedTools":["Bash(git","commit:*)"],"isInternal":false,"tokens":171,"sizeBytes":693},{"name":".cursorrules","path":".cursorrules (react-graphql-apollo-client-cursorrules-prompt-file.mdc)","rawUrl":"https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/react-graphql-apollo-client-cursorrules-prompt-file.mdc","title":"Awesome Cursorrules: react graphql apollo client","category":"cursor-rule","format":"markdown","content":"---\ndescription: \"Cursor rules for GraphQL development with Apollo Client integration.\"\nglobs: **/*\nalwaysApply: false\n---\n// React + GraphQL (Apollo Client) .cursorrules\n\n// Prefer functional components with hooks\n\nconst preferFunctionalComponents = true;\n\n// GraphQL and Apollo Client best practices\n\nconst graphqlBestPractices = [\n  \"Use Apollo Client for state management and data fetching\",\n  \"Implement query components for data fetching\",\n  \"Utilize mutations for data modifications\",\n  \"Use fragments for reusable query parts\",\n  \"Implement proper error handling and loading states\",\n];\n\n// Folder structure\n\nconst folderStructure = `\nsrc/\n  components/\n  graphql/\n    queries/\n    mutations/\n    fragments/\n  hooks/\n  pages/\n  utils/\n`;\n\n// Additional instructions\n\nconst additionalInstructions = `\n1. Use Apollo Provider at the root of your app\n2. Implement custom hooks for Apollo operations\n3. Use TypeScript for type safety with GraphQL operations\n4. Utilize Apollo Client's caching capabilities\n5. Implement proper error boundaries for GraphQL errors\n6. Use Apollo Client DevTools for debugging\n7. Follow naming conventions for queries, mutations, and fragments\n`;\n","tokens":237,"sizeBytes":1179}],"systemPromptSnippet":"<yakaai_skills repo=\"apollographql/apollo-client\">\n<!-- File: CLAUDE.md (Tokens: ~1092 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Apollo Client Overview\n\nApollo Client is a comprehensive caching GraphQL client for TypeScript/JavaScript. The project is maintained at version 4.x.x and uses a monorepo structure with npm workspaces.\n\n## Common Development Commands\n\n### Build Commands\n\n- `npm run build` - Build the entire project\n- `npm run build -w codegen` - Build specific workspace (e.g., codegen)\n\n### Testing Commands\n\n- `npm test` - Run all tests with Jest\n- `npm run test:type-benches` - Run type benchmarks\n- `npm run test:memory` - Run memory tests\n\n### Code Quality Commands\n\n- `npm run typecheck` - Run TypeScript type checking\n- `npm run lint` - Run ESLint with extended rules\n- `npm run format` - Format code with Prettier\n- `npm run check:format` - Check if code is formatted correctly\n- `npm run knip` - Check for unused files/exports\n- `npm run madge` - Check for circular dependencies\n\n### Documentation & API\n\n- `npm run extract-api` - Generate API documentation\n- `npm run typedoc` - Generate TypeDoc documentation\n- `npm run docmodel` - Generate API documentation model\n\n### Release & Publishing\n\n- `npx changeset` - Create a changeset for your changes\n\n## High-Level Architecture\n\n### Core Modules\n\n1. **Core (`src/core/`)** - Main Apollo Client implementation\n\n   - `ApolloClient.ts` - Main client class\n   - `ObservableQuery.ts` - Observable query implementation\n   - `QueryManager.ts` - Query lifecycle management\n\n2. **Cache (`src/cache/`)** - Caching layer\n\n   - `inmemory/` - InMemoryCache implementation (default cache)\n   - `core/` - Base cache abstractions\n\n3. **Link (`src/link/`)** - Network layer abstraction\n\n   - `http/` - HTTP link for GraphQL over HTTP\n   - `batch/` - Batch link for combining queries\n   - `error/` - Error handling link\n   - `schema/` - Schema link for local execution\n   - `ws/` - WebSocket link for subscriptions\n\n4. **React Integration (`src/react/`)** - React hooks and components\n\n   - `hooks/` - React hooks (useQuery, useMutation, etc.)\n   - `ssr/` - Server-side rendering support\n   - `context/` - React context providers\n\n5. **Utilities (`src/utilities/`)** - Shared utilities\n\n   - `graphql/` - GraphQL document utilities\n   - `internal/` - Internal utilities\n   - `invariant/` - Error handling utilities\n\n6. **Testing (`src/testing/`)** - Testing utilities\n\n   - `core/` - Core testing utilities\n   - `react/` - React testing utilities (MockedProvider)\n\n7. **Masking (`src/masking/`)** - Data masking for fragment colocation\n\n8. **Local State (`src/local-state/`)** - Local state management\n\n### TypeScript Configuration\n\n- Uses TypeScript 5.7.3 with strict mode enabled\n- Module resolution: NodeNext\n- Target: ESNext\n- Separate tsconfig files for different purposes:\n  - `tsconfig.json` - Main configuration\n  - `tsconfig.build.json` - Build configuration\n  - `tsconfig.tests.json` - Test configuration\n\n### Testing Infrastructure\n\n- Jest with ts-jest for testing\n- Custom test environment extending jsdom\n- React Testing Library for React component tests\n- Multiple React version testing (17, 18, 19)\n- Coverage reporting with lcov\n\n### Build System\n\n- Uses custom build script (`config/build.ts`)\n- API Extractor for API documentation\n- Size limit checking for bundle size\n- Source map verification\n- Publint for package publishing validation\n\n### Code Organization Patterns\n\n1. **Exports** - All public exports go through index files\n2. **Internal APIs** - Use `@apollo/client/*/internal` paths\n3. **Environment-specific builds** - Production/development variants\n4. **Type safety** - Heavy use of TypeScript generics and type inference\n5. **Reactive programming** - Uses RxJS for observables\n6. **Immutability** - Emphasizes immutable data structures\n\n### Contributing Guidelines\n\n- All changes require a changeset (`npx changeset`)\n- PRs should include tests for bug fixes\n- Follow existing code style (enforced by ESLint/Prettier)\n- Large features need design discussion first\n- Code review required from core contributors\n\n### Important Notes\n\n- This is a monorepo with workspaces: main package, codegen, and codemods\n- The project is currently at version 4.x.x (release candidate)\n- Supports multiple peer dependencies (React 17/18/19, GraphQL 16)\n- Uses patch-package for dependency patches\n- Has extensive CI checks that must pass before merging\n\n## Additional Instructions\n\n@.claude/documentation.md\n@.claude/hooks.md\n\n<!-- File: docs/agent-skills/apollo-client/references/caching.md (Tokens: ~2653 | Category: anthropic-skill) -->\n# Caching Reference\n\n## Table of Contents\n\n- [InMemoryCache Setup](#inmemorycache-setup)\n- [Cache Normalization](#cache-normalization)\n- [Type Policies](#type-policies)\n- [Field Policies](#field-policies)\n- [Pagination](#pagination)\n- [Cache Manipulation](#cache-manipulation)\n- [Garbage Collection](#garbage-collection)\n\n## InMemoryCache Setup\n\n### Basic Configuration\n\n```typescript\nimport { InMemoryCache } from \"@apollo/client\";\n\nconst cache = new InMemoryCache({\n  // Custom type policies\n  typePolicies: {\n    Query: {\n      fields: {\n        // Query-level field policies\n      },\n    },\n    User: {\n      keyFields: [\"id\"],\n      fields: {\n        // User-level field policies\n      },\n    },\n  },\n\n  // Custom type name handling (rare)\n  possibleTypes: {\n    Character: [\"Human\", \"Droid\"],\n    Node: [\"User\", \"Post\", \"Comment\"],\n  },\n});\n```\n\n### Constructor Options\n\n```typescript\nnew InMemoryCache({\n  // Define how types are identified in cache\n  typePolicies: {\n    /* ... */\n  },\n\n  // Interface/union type mappings between supertypes and their subtypes\n  possibleTypes: {\n    /* ... */\n  },\n\n  // Custom function to generate cache IDs (rare)\n  dataIdFromObject: (object) => {\n    if (object.__typename === \"Book\") {\n      return `Book:${object.isbn}`;\n    }\n    return defaultDataIdFromObject(object);\n  },\n});\n```\n\n## Cache Normalization\n\nApollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.\n\n### How Normalization Works\n\n```graphql\n# Query\nquery GetPost {\n  post(id: \"1\") {\n    id\n    title\n    author {\n      id\n      name\n    }\n  }\n}\n```\n\n```typescript\n// Normalized cache structure\n{\n  'Post:1': {\n    __typename: 'Post',\n    id: '1',\n    title: 'Hello World',\n    author: { __ref: 'User:42' }\n  },\n  'User:42': {\n    __typename: 'User',\n    id: '42',\n    name: 'John'\n  },\n  ROOT_QUERY: {\n    'post({\"id\":\"1\"})': { __ref: 'Post:1' }\n  }\n}\n```\n\n### Benefits of Normalization\n\n1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update\n2. **Deduplication**: Same objects aren't stored multiple times\n3. **Efficient updates**: Only changed objects trigger re-renders\n\n## Type Policies\n\n### keyFields\n\nCustomize how objects are identified in the cache.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Use ISBN instead of id for books\n    Book: {\n      keyFields: [\"isbn\"],\n    },\n\n    // Composite key\n    UserSession: {\n      keyFields: [\"userId\", \"deviceId\"],\n    },\n\n    // Nested key\n    Review: {\n      keyFields: [\"book\", [\"isbn\"], \"reviewer\", [\"id\"]],\n    },\n\n    // No key fields (singleton, only one object in cache per type)\n    AppSettings: {\n      keyFields: [],\n    },\n\n    // Disable normalization (objects of this type will be stored with their\n    // parent entity. The same object might end up multiple times in the cache\n    // and run out of sync. Use with caution, only if this object really relates\n    // to a property of their parent entity and cannot exist on its own.)\n    Address: {\n      keyFields: false,\n    },\n  },\n});\n```\n\n### merge Functions\n\nControl how incoming data merges with existing data.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Deep merge profile object\n        profile: {\n          merge: true, // Shorthand for deep merge\n        },\n\n        // Custom merge logic\n        notifications: {\n          merge(existing = [], incoming, { mergeObjects }) {\n            // Prepend new notifications\n            return [...incoming, ...existing];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## Field Policies\n\n### read Function\n\nTransform cached data when reading.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Computed field\n        fullName: {\n          read(_, { readField }) {\n            const firstName = readField(\"firstName\");\n            const lastName = readField(\"lastName\");\n            return `${firstName} ${lastName}`;\n          },\n        },\n\n        // Transform existing field\n        birthDate: {\n          read(existing) {\n            return existing ? new Date(existing) : null;\n          },\n        },\n\n        // Default value\n        role: {\n          read(existing = \"USER\") {\n            return existing;\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### merge Function\n\nControl how incoming data is stored.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Accumulate items instead of replacing\n        friends: {\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n\n        // Merge objects deeply\n        settings: {\n          merge(existing, incoming, { mergeObjects }) {\n            return mergeObjects(existing, incoming);\n          },\n        },\n      },\n    },\n\n    Query: {\n      fields: {\n        // Merge paginated results\n        posts: {\n          keyArgs: [\"category\"], // Only category affects cache key\n          merge(existing = { items: [] }, incoming) {\n            return {\n              ...incoming,\n              items: [...existing.items, ...incoming.items],\n            };\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### keyArgs\n\nControl which arguments affect cache storage.\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        // Different cache entry per userId only\n        // (limit, offset don't create new entries)\n        userPosts: {\n          keyArgs: [\"userId\"],\n        },\n\n        // No arguments affect cache key\n        // (useful for pagination)\n        feed: {\n          keyArgs: false,\n        },\n\n        // Nested argument\n        search: {\n          keyArgs: [\"filter\", [\"category\", \"status\"]],\n        },\n      },\n    },\n  },\n});\n```\n\n## Pagination\n\n### Offset-Based Pagination\n\n```typescript\nimport { offsetLimitPagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: offsetLimitPagination(),\n\n        // With key arguments\n        userPosts: offsetLimitPagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Cursor-Based Pagination (Relay Style)\n\n```typescript\nimport { relayStylePagination } from \"@apollo/client/utilities\";\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        posts: relayStylePagination(),\n\n        // With key arguments\n        userPosts: relayStylePagination([\"userId\"]),\n      },\n    },\n  },\n});\n```\n\n### Custom Pagination\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        feed: {\n          keyArgs: false,\n\n          merge(existing, incoming, { args }) {\n            const merged = existing ? existing.slice(0) : [];\n            const offset = args?.offset ?? 0;\n\n            for (let i = 0; i < incoming.length; i++) {\n              merged[offset + i] = incoming[i];\n            }\n\n            return merged;\n          },\n\n          read(existing, { args }) {\n            const offset = args?.offset ?? 0;\n            const limit = args?.limit ?? existing?.length ?? 0;\n            return existing?.slice(offset, offset + limit);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### fetchMore for Pagination\n\n```tsx\nfunction PostList() {\n  const { data, fetchMore, loading } = useQuery(GET_POSTS, {\n    variables: { offset: 0, limit: 10 },\n  });\n\n  const loadMore = () => {\n    fetchMore({\n      variables: {\n        offset: data.posts.length,\n      },\n      // With proper type policies, no updateQuery needed\n    });\n  };\n\n  return (\n    <div>\n      {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}\n      <button onClick={loadMore} disabled={loading}>\n        Load More\n      </button>\n    </div>\n  );\n}\n```\n\n## Cache Manipulation\n\n### cache.readQuery\n\n```typescript\n// Read data from cache\nconst data = cache.readQuery({\n  query: GET_TODOS,\n});\n\n// With variables\nconst userData = cache.readQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n});\n```\n\n### cache.writeQuery\n\n```typescript\n// Write data to cache\ncache.writeQuery({\n  query: GET_TODOS,\n  data: {\n    todos: [\n      { __typename: \"Todo\", id: \"1\", text: \"Buy milk\", completed: false },\n    ],\n  },\n});\n\n// With variables\ncache.writeQuery({\n  query: GET_USER,\n  variables: { id: \"1\" },\n  data: {\n    user: { __typename: \"User\", id: \"1\", name: \"John\" },\n  },\n});\n```\n\n### cache.readFragment / cache.writeFragment\n\n```typescript\n// Read a specific object - use cache.identify for safety\nconst user = cache.readFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\nconst user = cache.readFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UserFragment on User {\n      id\n      name\n      email\n    }\n  `,\n});\n\n// Update a specific object\ncache.writeFragment({\n  id: cache.identify({ __typename: \"User\", id: \"1\" }),\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n\n// Apollo Client 4.1+: Use 'from' parameter (recommended)\ncache.writeFragment({\n  from: { __typename: \"User\", id: \"1\" },\n  fragment: gql`\n    fragment UpdateUser on User {\n      name\n    }\n  `,\n  data: {\n    name: \"Jane\",\n  },\n});\n```\n\n### cache.modify\n\n```typescript\n// Modify fields directly\ncache.modify({\n  id: cache.identify(user),\n  fields: {\n    // Set new value\n    name: () => \"New Name\",\n\n    // Transform existing value\n    postCount: (existing) => existing + 1,\n\n    // Delete field\n    temporaryField: (_, { DELETE }) => DELETE,\n\n    // Add to array\n    friends: (existing, { toReference }) => [\n      ...existing,\n      toReference({ __typename: \"User\", id: \"2\" }),\n    ],\n  },\n});\n```\n\n### cache.evict\n\n```typescript\n// Remove object from cache\ncache.evict({ id: \"User:1\" });\n\n// Remove specific field\ncache.evict({ id: \"User:1\", fieldName: \"friends\" });\n\n// Remove with broadcast (trigger re-renders)\ncache.evict({ id: \"User:1\", broadcast: true });\n```\n\n## Garbage Collection\n\n### Manual Garbage Collection\n\n```typescript\n// After evicting objects, clean up dangling references\ncache.evict({ id: \"User:1\" });\ncache.gc();\n```\n\n### Retaining Objects\n\n```typescript\n// Prevent objects from being garbage collected\nconst release = cache.retain(\"User:1\");\n\n// Later, allow GC\nrelease();\ncache.gc();\n```\n\n### Inspecting Cache\n\n```typescript\n// Get all cached data\nconst cacheContents = cache.extract();\n\n// Restore cache state\ncache.restore(previousCacheContents);\n\n// Get identified object cache key\nconst userId = cache.identify({ __typename: \"User\", id: \"1\" });\n// Returns: 'User:1'\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/error-handling.md (Tokens: ~2525 | Category: anthropic-skill) -->\n# Error Handling Reference (Apollo Client 4.x)\n\nNote that Apollo Client 4.x handles errors differently than Apollo Client 3.x.\nThis reference documents the updated error handling mechanisms, error types, and best practices for managing errors in your Apollo Client applications.\nFor older Apollo Client 3.x error handling documentation, see [Apollo Client 3.x Error Handling](https://www.apollographql.com/docs/react/v3/data/error-handling).\n\n## Table of Contents\n\n- [Understanding Errors](#understanding-errors)\n- [Error Types](#error-types)\n- [Identifying Error Types](#identifying-error-types)\n- [GraphQL Error Policies](#graphql-error-policies)\n- [Error Links](#error-links)\n- [Retry Link](#retry-link)\n- [Error Boundaries](#error-boundaries)\n\n## Understanding Errors\n\nErrors in Apollo Client fall into two main categories: **GraphQL errors** and **network errors**. Each category has specific error classes that provide detailed information about what went wrong.\n\n### GraphQL Errors\n\nGraphQL errors are related to server-side execution of a GraphQL operation:\n\n- **Syntax errors** (e.g., malformed query)\n- **Validation errors** (e.g., query includes a non-existent schema field)\n- **Resolver errors** (e.g., error while populating a query field)\n\nIf a syntax or validation error occurs, the server doesn't execute the operation. If resolver errors occur, the server can still return partial data.\n\nExample server response with GraphQL error:\n\n```json\n{\n  \"errors\": [\n    {\n      \"message\": \"Cannot query field \\\"nonexistentField\\\" on type \\\"Query\\\".\",\n      \"locations\": [{ \"line\": 2, \"column\": 3 }],\n      \"extensions\": {\n        \"code\": \"GRAPHQL_VALIDATION_FAILED\"\n      }\n    }\n  ],\n  \"data\": null\n}\n```\n\nIn Apollo Client 4.x, GraphQL errors are represented by the [`CombinedGraphQLErrors`](https://apollographql.com/docs/react/api/errors/CombinedGraphQLErrors) error type.\n\n### Network Errors\n\nNetwork errors occur when attempting to communicate with your GraphQL server:\n\n- `4xx` or `5xx` HTTP response status codes\n- Network unavailability\n- JSON parsing failures\n- Custom errors from Apollo Link request handlers\n\nNetwork errors might be represented by special error types, but if an api such as the `fetch` API throws a native error (e.g., `TypeError`), Apollo Client will pass it through as-is.\nThrown values that don't fulfill the standard `ErrorLike` interface are wrapped in the [`UnconventionalError`](https://apollographql.com/docs/react/api/errors/UnconventionalError) class, which fulfills the `ErrorLike` interface. As such, you can expect any error returned by Apollo Client to fulfill the `ErrorLike` interface.\n\n```ts\nexport interface ErrorLike {\n  message: string;\n  name: string;\n  stack?: string;\n}\n```\n\n## Error Types\n\nApollo Client 4.x provides specific error classes for different error scenarios:\n\n### CombinedGraphQLErrors\n\nRepresents GraphQL errors returned by the server. Most common error type in applications.\n\n```tsx\nimport { CombinedGraphQLErrors } from \"@apollo/client/errors\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, error } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // no need to check for nullishness of error, CombinedGraphQLErrors.is handles that\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    return (\n      <div>\n        {error.graphQLErrors.map((err, i) => (\n          <p key={i}>GraphQL Error: {err.message}</p>\n        ))}\n      </div>\n    );\n  }\n\n  return data ? <Profile user={data.user} /> : null;\n}\n```\n\n### CombinedProtocolErrors\n\nRepresents fatal transport-level errors during multipart HTTP subscription execution.\n\n### ServerError\n\nOccurs when the server responds with a non-200 HTTP status code.\n\n```tsx\nimport { ServerError } from \"@apollo/client/errors\";\n\nif (ServerError.is(error)) {\n  console.error(\"Server error:\", error.statusCode, error.result);\n}\n```\n\n### ServerParseError\n\nOccurs when the server response cannot be parsed as valid JSON.\n\n```tsx\nimport { ServerParseError } from \"@apollo/client/errors\";\n\nif (ServerParseError.is(error)) {\n  console.error(\"Invalid JSON response:\", error.bodyText);\n}\n```\n\n### LocalStateError\n\nRepresents errors in local state configuration or execution.\n\n### UnconventionalError\n\nWraps non-standard errors (e.g., thrown symbols or objects) to ensure consistent error handling.\n\n## Identifying Error Types\n\nEvery Apollo Client error class provides a static `is` method that reliably determines whether an error is of that specific type. This is more robust than `instanceof` because it avoids false positives/negatives.\n\n```ts\nimport {\n  CombinedGraphQLErrors,\n  CombinedProtocolErrors,\n  LocalStateError,\n  ServerError,\n  ServerParseError,\n  UnconventionalError,\n  ErrorLike,\n} from \"@apollo/client/errors\";\n\n// Anything returned in the `error` field of Apollo Client hooks or methods is of type `ErrorLike` or `undefined`.\nfunction handleError(error?: ErrorLike) {\n  if (CombinedGraphQLErrors.is(error)) {\n    // Handle GraphQL errors\n    console.error(\"GraphQL errors:\", error.graphQLErrors);\n  } else if (CombinedProtocolErrors.is(error)) {\n    // Handle multipart subscription protocol errors\n  } else if (LocalStateError.is(error)) {\n    // Handle errors thrown by the LocalState class\n  } else if (ServerError.is(error)) {\n    // Handle server HTTP errors\n    console.error(\"Server error:\", error.statusCode);\n  } else if (ServerParseError.is(error)) {\n    // Handle JSON parse errors\n  } else if (UnconventionalError.is(error)) {\n    // Handle errors thrown by irregular types\n  } else if (error) {\n    // Handle other errors\n  }\n}\n```\n\n## GraphQL Error Policies\n\nIf a GraphQL operation produces errors, the server's response might still include partial data:\n\n```json\n{\n  \"data\": {\n    \"getInt\": 12,\n    \"getString\": null\n  },\n  \"errors\": [\n    {\n      \"message\": \"Failed to get string!\"\n    }\n  ]\n}\n```\n\nBy default, Apollo Client throws away partial data and populates the `error` field. You can use partial results by defining an **error policy**:\n\n| Policy   | Description                                                                                                                                                                  |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `none`   | (Default) If the response includes errors, they are returned in `error` and response `data` is set to `undefined` even if the server returns `data`.                         |\n| `ignore` | Errors are ignored (`error` is not populated), and any returned `data` is cached and rendered as if no errors occurred. `data` may be `undefined` if a network error occurs. |\n| `all`    | Both `data` and `error` are populated and any returned `data` is cached, enabling you to render both partial results and error information.                                  |\n\n### Setting an Error Policy\n\n```tsx\nconst MY_QUERY = gql`\n  query WillFail {\n    badField # This field's resolver produces an error\n    goodField # This field is populated successfully\n  }\n`;\n\nfunction ShowingSomeErrors() {\n  const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: \"all\" });\n\n  if (loading) return <span>loading...</span>;\n\n  return (\n    <div>\n      <h2>Good: {data?.goodField}</h2>\n      {error && <pre>Bad: {error.message}</pre>}\n    </div>\n  );\n}\n```\n\n### Avoid setting a Global Error Policy\n\nWhile it is possible to set a global error policy using `defaultOptions`, in practice this is discouraged as it can lead to unexpected behavior and type safety issues. The return types of the TypeScript hooks may change depending on the `errorPolicy` passed into the hook, and this can conceptually not take global `defaultOptions` error policies into account. As such, it is best to set the `errorPolicy` per operation as needed.\n\n## Error Links\n\nThe `ErrorLink` can be used to e.g. log error globally or perform specific side effects based on errors happening.\n\nAn `ErrorLink` can't be used to swallow errors fully, but it can be used to retry an operation after handling an error, in which case the error wouldn't propagate. Otherwise, the most common use for `ErrorLink` is logging.\n\n```ts\nimport { ErrorLink } from \"@apollo/client/link/error\";\n\nconst errorLink = new ErrorLink(({ error, operation, forward }) => {\n  if (someCondition(error)) {\n    // Retry the request, returning the new observable\n    return forward(operation);\n  }\n\n  // Log the error for any unhandled GraphQL errors or network errors.\n  console.log(`[Error]: ${error.message}`);\n\n  // If nothing is returned from the error handler callback, the error will be\n  // emitted from the link chain as normal.\n});\n```\n\n### Retry Link\n\nAlternatively, you can use the `RetryLink` from `@apollo/client/link/retry` to implement retry logic for failed operations.\n\n```typescript\nimport { RetryLink } from \"@apollo/client/link/retry\";\n\nconst retryLink = new RetryLink({\n  delay: {\n    initial: 300,\n    max: Infinity,\n    jitter: true,\n  },\n  attempts: {\n    max: 5,\n    retryIf: (error, operation) => {\n      // Retry on network errors\n      return !!error && operation.operationName !== \"SensitiveOperation\";\n    },\n  },\n});\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  link: from([retryLink, errorLink, httpLink]),\n});\n```\n\n### Custom Retry Logic\n\n```typescript\nconst retryLink = new RetryLink({\n  attempts: (count, operation, error) => {\n    // Don't retry mutations\n    if (\n      operation.query.definitions.some(\n        (def) =>\n          def.kind === \"OperationDefinition\" && def.operation === \"mutation\"\n      )\n    ) {\n      return false;\n    }\n\n    // Retry up to 3 times on network errors\n    return count < 3 && !!error;\n  },\n  delay: (count) => {\n    // Exponential backoff\n    return Math.min(1000 * Math.pow(2, count), 30000);\n  },\n});\n```\n\n## Error Boundaries\n\nWhen using suspenseful hooks, you should use React Error Boundaries for graceful error handling.\n\n### Non-suspense per-Component Error Handling\n\n```tsx\nimport {\n  CombinedGraphQLErrors,\n  ServerError,\n  ServerParseError,\n} from \"@apollo/client/errors\";\n\nfunction SafeUserList() {\n  const { data, error, loading, refetch } = useQuery(GET_USERS, {\n    errorPolicy: \"all\",\n    notifyOnNetworkStatusChange: true,\n  });\n\n  // Handle network errors\n  if (ServerError.is(error) || ServerParseError.is(error)) {\n    return (\n      <Alert severity=\"error\">\n        <AlertTitle>Connection Error</AlertTitle>\n        Failed to load users. Please check your internet connection.\n        <Button onClick={() => refetch()}>Retry</Button>\n      </Alert>\n    );\n  }\n\n  // Handle GraphQL errors but still show available data\n  return (\n    <div>\n      {CombinedGraphQLErrors.is(error) && (\n        <Alert severity=\"warning\">\n          Some data may be incomplete: {error.graphQLErrors[0].message}\n        </Alert>\n      )}\n\n      {loading && <LinearProgress />}\n\n      {data?.users && <UserList users={data.users} />}\n    </div>\n  );\n}\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/fragments.md (Tokens: ~4955 | Category: anthropic-skill) -->\n# Fragments Reference\n\nGraphQL fragments define a set of fields for a specific type. In Apollo Client, fragments are especially powerful when colocated with components to define each component's data requirements independently, creating a clear separation of concerns and enabling better component composition.\n\n## Table of Contents\n\n- [What Are Fragments](#what-are-fragments)\n- [Basic Fragment Syntax](#basic-fragment-syntax)\n- [Fragment Colocation](#fragment-colocation)\n- [Fragment Reading Hooks](#fragment-reading-hooks)\n- [Data Masking](#data-masking)\n- [Fragment Registry](#fragment-registry)\n- [TypeScript Integration](#typescript-integration)\n- [Best Practices](#best-practices)\n\n## What Are Fragments\n\nA GraphQL fragment defines a set of fields for a specific GraphQL type. Fragments are defined on a specific GraphQL type and can be included in operations using the spread operator (`...`).\n\nIn Apollo Client, fragments serve a specific purpose:\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment. This allows components to independently evolve their data requirements without creating artificial dependencies between unrelated parts of your application.\n\nFragments enable:\n\n1. **Component colocation**: Define the exact data requirements for a component alongside the component code\n2. **Independent evolution**: Change a component's data needs without affecting other components\n3. **Code organization**: Compose fragments together to build complete queries that mirror your component hierarchy\n\n## Basic Fragment Syntax\n\n### Defining a Fragment\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n    avatarUrl\n  }\n`;\n```\n\nEvery fragment includes:\n\n- A unique name (`UserFields`)\n- The type it operates on (`User`)\n- The fields to select\n\n### Using Fragments in Queries\n\nInclude fragments in queries using the spread operator:\n\n```typescript\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserFields\n    }\n  }\n\n  ${USER_FRAGMENT}\n`;\n```\n\nWhen using GraphQL Code Generator with the recommended configuration (typescript, typescript-operations, and typed-document-node plugins), fragments defined in your source files are automatically picked up and generated into typed document nodes. The generated fragment documents already include the fragment definition, so you don't need to interpolate them manually into queries.\n\n## Fragment Colocation\n\nFragment colocation is the practice of defining fragments in the same file as the component that uses them. This creates a clear contract between components and their data requirements.\n\n### Why Colocate Fragments\n\n- **Locality**: Data requirements live next to the code that uses them\n- **Maintainability**: Changes to component UI and data needs happen together\n- **Type safety**: TypeScript can infer exact types from colocated fragments\n- **Independence**: Components can evolve their data requirements without affecting other components\n\n### Colocation Pattern\n\nThe recommended pattern for colocating fragments with components:\n\n```tsx\nimport { gql, FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\n\n// Fragment definition\n// This will be picked up by Codegen to create `UserCard_UserFragmentDoc` in `./fragments.generated.ts`.\n// As that generated fragment document is correctly typed, we use that in the code going forward.\n// This fragment will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n}\n\n// This has been created from above fragment definition by CodeGen and is a correctly typed `TypedDocumentNode`\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated.ts\";\n\n// Component receives the (partially masked) parent object\nexport function UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a subscription to the fragment in the cache\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return (\n    <div>\n      <img src={data.avatarUrl} alt={data.name} />\n      <h2>{data.name}</h2>\n      <p>{data.email}</p>\n    </div>\n  );\n}\n```\n\n### Naming Convention\n\nA suggested naming pattern for fragments follows this convention:\n\n```text\n{ComponentName}_{propName}\n```\n\nWhere `propName` is the name of the prop the component receives containing the fragment data.\n\nExamples:\n\n- `UserCard_user` - Fragment for the `user` prop in the UserCard component\n- `PostList_posts` - Fragment for the `posts` prop in the PostList component\n- `CommentItem_comment` - Fragment for the `comment` prop in the CommentItem component\n\nThis convention makes it clear which component owns which fragment. However, you can choose a different naming convention based on your project's needs.\n\n**Note**: A component might accept fragment data through multiple props, in which case it would have multiple associated fragments. For example, a `CommentCard` component might accept both a `comment` prop and an `author` prop, resulting in `CommentCard_comment` and `CommentCard_author` fragments.\n\n### Composing Fragments\n\nParent components compose child fragments to build complete queries:\n\n```tsx\n// Child component\nimport { gql } from \"@apollo/client\";\n\nif (false) {\n  gql`\n    fragment UserAvatar_user on User {\n      id\n      avatarUrl\n      name\n    }\n  `;\n}\n\n// Parent component composes child fragments\nif (false) {\n  gql`\n    fragment UserProfile_user on User {\n      id\n      name\n      email\n      bio\n      ...UserAvatar_user\n    }\n  `;\n}\n\n// Page-level query composes all fragments\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserProfile_user\n      }\n    }\n  `;\n}\n```\n\nThis creates a hierarchy that mirrors your component tree.\n\n## Fragment Reading Hooks\n\nApollo Client provides hooks to read fragment data within components. These hooks work with data masking to ensure components only access the data they explicitly requested.\n\n### useSuspenseFragment\n\nFor components using Suspense and concurrent features:\n\n```tsx\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  return <div>{data.name}</div>;\n}\n```\n\n### useFragment\n\nFor components not using Suspense:\n\n```tsx\nimport { useFragment } from \"@apollo/client/react\";\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data, complete } = useFragment({\n    fragment: UserCard_UserFragmentDoc,\n    fragmentName: \"UserCard_user\",\n    from: user,\n  });\n\n  if (!complete) {\n    return <div>Loading...</div>;\n  }\n\n  return <div>{data.name}</div>;\n}\n```\n\nThe `complete` field indicates whether all fragment data is available in the cache.\n\n### Hook Options\n\nBoth hooks accept these options:\n\n```typescript\n{\n  // The fragment document (required)\n  fragment: TypedDocumentNode,\n\n  // The fragment name (optional in most cases)\n  // Only required if the fragment document contains multiple definitions\n  fragmentName?: string,\n\n  // The source data containing the fragment (required)\n  // Can be a single object or an array of objects\n  from: FragmentType<typeof fragment> | Array<FragmentType<typeof fragment>>,\n\n  // Variables for the fragment (optional)\n  variables?: Variables,\n}\n```\n\nWhen `from` is an array, the hook returns an array of results, allowing you to read fragments from multiple objects efficiently. **Note**: Array support for the `from` parameter was added in Apollo Client 4.1.0.\n\n## Data Masking\n\nData masking is a feature that prevents components from accessing data they didn't explicitly request through their fragments. This enforces proper data boundaries and prevents over-rendering.\n\n### Enabling Data Masking\n\nEnable data masking when creating your Apollo Client:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true, // Enable data masking\n});\n```\n\n### How Data Masking Works\n\nWith data masking enabled:\n\n1. Fragments return opaque `FragmentType` objects\n2. Components must use `useFragment` or `useSuspenseFragment` to unmask data\n3. Components can only access fields defined in their own fragments\n4. TypeScript enforces these boundaries at compile time\n\nWithout data masking:\n\n```tsx\n// ❌ Without data masking - component can access any data from parent\nfunction UserCard({ user }: { user: User }) {\n  // Can access any User field, even if not in fragment\n  return <div>{user.privateData}</div>;\n}\n```\n\nWith data masking:\n\n```tsx\n// ✅ With data masking - component can only access its fragment data\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // TypeScript error: 'privateData' doesn't exist on fragment type\n  // return <div>{data.privateData}</div>;\n\n  // Only fields from the fragment are accessible\n  return <div>{data.name}</div>;\n}\n```\n\n### Benefits of Data Masking\n\n- **Prevents over-rendering**: Components only re-render when their specific data changes\n- **Enforces boundaries**: Components can't accidentally depend on data they don't own\n- **Better refactoring**: Safe to modify parent queries without breaking child components\n- **Type safety**: TypeScript catches attempts to access unavailable fields\n\n## Fragment Registry\n\nThe fragment registry is an **alternative approach** to GraphQL Code Generator's automatic fragment inlining by name. It allows you to register fragments globally, making them available throughout your application by name reference.\n\n**Important**: GraphQL Code Generator automatically inlines fragments by name wherever they're used in your queries. Either approach is sufficient on its own—**you don't need to combine them**.\n\n### Creating a Fragment Registry\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createFragmentRegistry } from \"@apollo/client/cache\";\n\nexport const fragmentRegistry = createFragmentRegistry();\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache({\n    fragments: fragmentRegistry,\n  }),\n});\n```\n\n### Registering Fragments\n\nRegister fragments after defining them:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { fragmentRegistry } from \"./apollo/client\";\n\nconst USER_FRAGMENT = gql`\n  fragment UserFields on User {\n    id\n    name\n    email\n  }\n`;\n\nfragmentRegistry.register(USER_FRAGMENT);\n```\n\nWith colocated fragments:\n\n```tsx\nimport { fragmentRegistry } from \"@/apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Register the fragment globally\nfragmentRegistry.register(UserCard_UserFragmentDoc);\n```\n\n### Using Registered Fragments\n\nOnce registered, fragments can be referenced by name in queries without explicit imports:\n\n```tsx\n// Fragment is available by name because it's registered\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      ...UserCard_user\n    }\n  }\n`;\n```\n\n### Approaches for Fragment Composition\n\nThere are three approaches to make child fragments available in parent queries:\n\n1. **GraphQL Code Generator inlining** (Recommended): CodeGen automatically inlines fragments by name. No manual work needed—just reference fragments by name in your queries.\n\n2. **Fragment Registry**: Manually register fragments to make them available by name. Useful for runtime scenarios where CodeGen isn't available.\n\n3. **Manual interpolation**: Explicitly import and interpolate child fragments into parent fragments:\n\n   ```typescript\n   import { CHILD_FRAGMENT } from \"./ChildComponent\";\n\n   const PARENT_FRAGMENT = gql`\n     fragment Parent_data on Data {\n       field\n       ...Child_data\n     }\n     ${CHILD_FRAGMENT}\n   `;\n   ```\n\n### Pros and Cons\n\n**GraphQL Code Generator inlining**:\n\n- ✅ Less work: Automatic, no manual registration needed\n- ❌ Larger bundle: Fragments are inlined into every query that uses them\n\n**Fragment Registry**:\n\n- ✅ Smaller bundle: Fragments are registered once, referenced by name\n- ❌ More work: Requires manual registration of each fragment\n- ❌ May cause issues with lazy-loaded modules if the module is not loaded before the query is executed\n- ✅ Best for deeply nested component trees where bundle size matters\n\n**Manual interpolation**:\n\n- ❌ Most work: Manual imports and interpolation required\n- ✅ Explicit: Clear fragment dependencies in code\n\n### Recommendation\n\nFor most applications using GraphQL Code Generator (as shown in this guide), **use the automatic inlining**—it requires no additional setup and works seamlessly. Consider the fragment registry only if bundle size becomes a concern in applications with deeply nested component trees.\n\n## TypeScript Integration\n\nApollo Client provides strong TypeScript support for fragments through GraphQL Code Generator.\n\n### Generated Types\n\nGraphQL Code Generator produces typed fragment documents:\n\n```typescript\n// Generated file: fragments.generated.ts\nexport type UserCard_UserFragment = {\n  __typename: \"User\";\n  id: string;\n  name: string;\n  email: string;\n  avatarUrl: string;\n} & { \" $fragmentName\"?: \"UserCard_UserFragment\" };\n\nexport const UserCard_UserFragmentDoc: TypedDocumentNode<\n  UserCard_UserFragment,\n  never\n>;\n```\n\n### Type-Safe Fragment Usage\n\nUse `FragmentType` to accept masked fragment data:\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // 'data' is fully typed as UserCard_UserFragment\n  return <div>{data.name}</div>;\n}\n```\n\n### Fragment Type Inference\n\nTypeScript infers types from fragment documents automatically:\n\n```tsx\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\n// Types are inferred from the fragment\nconst { data } = useSuspenseFragment({\n  fragment: UserCard_UserFragmentDoc,\n  from: user,\n});\n\n// data.name is string\n// data.email is string\n// data.nonExistentField is a TypeScript error\n```\n\n### Parent-Child Type Safety\n\nWhen passing fragment data from parent to child:\n\n```tsx\n// Parent query\nconst { data } = useSuspenseQuery(GET_USER);\n\n// TypeScript ensures the query includes UserCard_user fragment\n// before allowing it to be passed to UserCard\n<UserCard user={data.user} />;\n```\n\n## Best Practices\n\n### Prefer Colocation Over Reuse\n\n**Fragments are for colocation, not reuse.** Each component should declare its data needs in a dedicated fragment, even if multiple components currently need the same fields.\n\nSharing fragments between components just because they happen to need the same fields today creates artificial dependencies. When one component's requirements change, the shared fragment must be updated, causing all components using it to over-fetch data they don't need.\n\n```tsx\n// ✅ Good: Each component has its own fragment\nif (false) {\n  gql`\n    fragment UserCard_user on User {\n      id\n      name\n      email\n      avatarUrl\n    }\n  `;\n\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n    }\n  `;\n}\n\n// If UserCard later needs 'bio', only UserCard_user changes\n// UserListItem doesn't over-fetch 'bio'\n```\n\n```tsx\n// ❌ Avoid: Sharing a generic fragment across components\nconst COMMON_USER_FIELDS = gql`\n  fragment CommonUserFields on User {\n    id\n    name\n    email\n  }\n`;\n\n// UserCard and UserListItem both use CommonUserFields\n// When UserCard needs 'bio', adding it to CommonUserFields\n// causes UserListItem to over-fetch unnecessarily\n```\n\nThis independence allows each component to evolve its data requirements without affecting unrelated parts of your application.\n\n### One Query Per Page\n\nCompose all page data requirements into a single query at the page level:\n\n```tsx\n// ✅ Good: Single page-level query\nif (false) {\n  gql`\n    query UserProfilePage($id: ID!) {\n      user(id: $id) {\n        ...UserHeader_user\n        ...UserPosts_user\n        ...UserFriends_user\n      }\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Multiple queries in different components\nfunction UserProfile() {\n  const { data: userData } = useQuery(GET_USER);\n  const { data: postsData } = useQuery(GET_USER_POSTS);\n  const { data: friendsData } = useQuery(GET_USER_FRIENDS);\n  // ...\n}\n```\n\n### Use Fragment-Reading Hooks in Components\n\nNon-page components should use `useFragment` or `useSuspenseFragment`:\n\n```tsx\n// ✅ Good: Component reads fragment data\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n  return <div>{data.name}</div>;\n}\n```\n\n```tsx\n// ❌ Avoid: Component uses query hook\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, { variables: { id: userId } });\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Request Only Required Fields\n\nKeep fragments minimal and only request fields the component actually uses:\n\n```tsx\n// ✅ Good: Only necessary fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n    }\n  `;\n}\n```\n\n```tsx\n// ❌ Avoid: Requesting unused fields\nif (false) {\n  gql`\n    fragment UserListItem_user on User {\n      id\n      name\n      email\n      bio\n      friends {\n        id\n        name\n      }\n      posts {\n        id\n        title\n      }\n    }\n  `;\n}\n```\n\n### Use @defer for Below-the-Fold Content\n\nThe `@defer` directive allows you to defer loading of non-critical fields, enabling faster initial page loads by prioritizing essential data. The deferred fields are delivered via incremental delivery and arrive after the non-deferred data, allowing the UI to progressively render as data becomes available.\n\nDefer slow fields that aren't immediately visible:\n\n```tsx\nif (false) {\n  gql`\n    query ProductPage($id: ID!) {\n      product(id: $id) {\n        id\n        name\n        price\n        ...ProductReviews_product @defer\n      }\n    }\n  `;\n}\n```\n\nThis allows the page to render quickly while reviews load in the background.\n\n### Handle Client-Only Fields\n\nUse the `@client` directive for fields resolved locally:\n\n```tsx\nif (false) {\n  gql`\n    fragment TodoItem_todo on Todo {\n      id\n      text\n      completed\n      isSelected @client\n    }\n  `;\n}\n```\n\n### Enable Data Masking for New Applications\n\nAlways enable data masking in new applications:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  dataMasking: true,\n});\n```\n\nThis enforces proper boundaries from the start and prevents accidental coupling between components.\n\n## Apollo Client Data Masking vs GraphQL-Codegen Fragment Masking\n\nApollo Client's data masking and GraphQL Code Generator's fragment masking are different features that serve different purposes:\n\n### GraphQL-Codegen Fragment Masking\n\nGraphQL Code Generator's fragment masking (when using the client preset) is a **type-level** feature:\n\n- Masks data only at the TypeScript type level\n- The actual runtime data remains fully accessible on the object\n- Using their `useFragment` hook simply \"unmasks\" the data on a type level\n- Does not prevent accidental access to data at runtime\n- Parent components receive all data and pass it down\n- This means the parent component has to be subscribed to all data\n\n### Apollo Client Data Masking\n\nApollo Client's data masking is a **runtime** feature with significant performance benefits:\n\n- Removes data at the runtime level, not just the type level\n- The `useFragment` and `useSuspenseFragment` hooks create cache subscriptions\n- Parent objects are sparse and only contain unmasked data\n- Prevents accidental access to data that should be masked\n\n### Key Benefits of Apollo Client Data Masking\n\n**1. No Accidental Data Access**\n\nWith runtime data masking, masked fields are not present in the parent object at all. You cannot accidentally access them, even if you bypass TypeScript type checking.\n\n**2. Fewer Re-renders**\n\nApollo Client's approach creates more efficient subscriptions:\n\n- **Without data masking**: Parent component subscribes to all fields (including masked ones). When a masked child field changes, the parent re-renders to pass that runtime data down the tree.\n- **With data masking**: Parent component only subscribes to its own unmasked fields. Subscriptions on masked fields happen lower in the React component tree when the child component calls `useSuspenseFragment`. When a masked field changes, only the child component that subscribed to it re-renders.\n\n### Example\n\n```tsx\nimport { FragmentType } from \"@apollo/client\";\nimport { useSuspenseQuery, useSuspenseFragment } from \"@apollo/client/react\";\nimport { UserCard_UserFragmentDoc } from \"./fragments.generated\";\n\nfunction ParentComponent() {\n  const { data } = useSuspenseQuery(GET_USER);\n\n  // With Apollo Client data masking:\n  // - data.user only contains unmasked fields\n  // - Parent doesn't re-render when child-specific fields change\n\n  return <UserCard user={data.user} />;\n}\n\nfunction UserCard({\n  user,\n}: {\n  user: FragmentType<typeof UserCard_UserFragmentDoc>;\n}) {\n  // Creates a cache subscription specifically for UserCard_user fields\n  const { data } = useSuspenseFragment({\n    fragment: UserCard_UserFragmentDoc,\n    from: user,\n  });\n\n  // Only this component re-renders when these fields change\n  return <div>{data.name}</div>;\n}\n```\n\nThis granular subscription approach improves performance in large applications with deeply nested component trees.\n\n<!-- File: docs/agent-skills/apollo-client/references/integration-client.md (Tokens: ~2065 | Category: anthropic-skill) -->\n# Apollo Client Integration for Client-Side Apps\n\nThis guide covers setting up Apollo Client in client-side React applications without server-side rendering (SSR). This includes applications using Vite, Parcel, Create React App, or other bundlers that don't implement SSR.\n\nFor applications with SSR, use one of the framework-specific integration guides instead:\n\n- [Next.js App Router](integration-nextjs.md)\n- [React Router Framework Mode](integration-react-router.md)\n- [TanStack Start](integration-tanstack-start.md)\n\n## Installation\n\n```bash\nnpm install @apollo/client graphql rxjs\n```\n\n## TypeScript Code Generation (optional but recommended)\n\nFor type-safe GraphQL operations with TypeScript, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup Steps\n\n### Step 1: Create Client\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\n\n// Recommended: Use HttpOnly cookies for authentication\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  credentials: \"include\", // Sends cookies with requests (secure when using HttpOnly cookies)\n});\n\nconst client = new ApolloClient({\n  link: httpLink,\n  cache: new InMemoryCache(),\n});\n```\n\nIf you need manual token management (less secure, only when HttpOnly cookies aren't available):\n\n```typescript\nimport { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\n\nconst httpLink = new HttpLink({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n});\n\nconst authLink = new SetContextLink(({ headers }) => {\n  const token = localStorage.getItem(\"token\");\n  return {\n    headers: {\n      ...headers,\n      authorization: token ? `Bearer ${token}` : \"\",\n    },\n  };\n});\n\nconst client = new ApolloClient({\n  link: authLink.concat(httpLink),\n  cache: new InMemoryCache(),\n});\n```\n\n### Step 2: Setup Provider\n\n```tsx\nimport { ApolloProvider } from \"@apollo/client\";\nimport App from \"./App\";\n\nfunction Root() {\n  return (\n    <ApolloProvider client={client}>\n      <App />\n    </ApolloProvider>\n  );\n}\n```\n\n### Step 3: Execute Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USERS = gql`\n  query GetUsers {\n    users {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserList() {\n  const { loading, error, data, dataState } = useQuery(GET_USERS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Basic Query Usage\n\n### Using Variables\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n> **Note for TypeScript users**: Use [`dataState`](https://www.apollographql.com/docs/react/data/typescript#type-narrowing-data-with-datastate) for more robust type safety and better type narrowing in Apollo Client 4.x.\n\n### TypeScript Integration\n\nFor complete examples with loading, error handling, and `dataState` for type narrowing, see [Basic Query Usage](#basic-query-usage) above.\n\n#### Usage with Generated Types\n\nFor type-safe operations with code generation, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\nQuick example:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\n// Define your query with the if (false) pattern for code generation\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n#### Usage with Manual Type Annotations\n\nIf not using code generation, define types alongside your queries using `TypedDocumentNode`:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetUserData {\n  user: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface GetUserVariables {\n  id: string;\n}\n\n// Types should always be defined via TypedDocumentNode alongside your queries/mutations, not at the useQuery/useMutation call site\nconst GET_USER: TypedDocumentNode<GetUserData, GetUserVariables> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // data.user is automatically typed from GET_USER\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Basic Mutation Usage\n\n```tsx\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\ninterface CreateUserMutation {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserMutationVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<\n  CreateUserMutation,\n  CreateUserMutationVariables\n> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (formData: FormData) => {\n    const { data } = await createUser({\n      variables: {\n        input: {\n          name: formData.get(\"name\") as string,\n          email: formData.get(\"email\") as string,\n        },\n      },\n    });\n    if (data) {\n      console.log(\"Created user:\", data.createUser);\n    }\n  };\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <input name=\"name\" placeholder=\"Name\" />\n      <input name=\"email\" placeholder=\"Email\" />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create User\"}\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n## Client Configuration Options\n\n```typescript\nconst client = new ApolloClient({\n  // Required: The cache implementation\n  cache: new InMemoryCache({\n    typePolicies: {\n      Query: {\n        fields: {\n          // Field-level cache configuration\n        },\n      },\n    },\n  }),\n\n  // Network layer\n  link: new HttpLink({ uri: \"/graphql\" }),\n\n  // Avoid defaultOptions if possible as they break TypeScript expectations.\n  // Configure options per-query/mutation instead for better type safety.\n  // defaultOptions: {\n  //   watchQuery: { fetchPolicy: 'cache-and-network' },\n  // },\n\n  // DevTools are enabled by default in development\n  // Only configure when enabling in production\n  devtools: {\n    enabled: true, // Only needed for production\n  },\n\n  // Custom name for this client instance\n  clientAwareness: {\n    name: \"web-client\",\n    version: \"1.0.0\",\n  },\n});\n```\n\n## Important Considerations\n\n1. **Choose Your Hook Strategy:** Decide if your application should be based on Suspense. If it is, use suspenseful hooks like `useSuspenseQuery` (see [Suspense Hooks guide](suspense-hooks.md)), otherwise use non-suspenseful hooks like `useQuery` (see [Queries guide](queries.md)).\n\n2. **Client-Side Only:** This setup is for client-side apps without SSR. The Apollo Client instance is created once and reused throughout the application lifecycle.\n\n3. **Authentication:** Prefer HttpOnly cookies with `credentials: \"include\"` in `HttpLink` options to avoid exposing tokens to JavaScript. If manual token management is necessary, use `SetContextLink` to dynamically add authentication headers from `localStorage` or other client-side storage.\n\n4. **Environment Variables:** Store your GraphQL endpoint URL in environment variables for different environments (development, staging, production).\n\n5. **Error Handling:** Always handle `loading` and `error` states when using `useQuery` or `useLazyQuery`. For Suspense-based hooks (`useSuspenseQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n\n<!-- File: docs/agent-skills/apollo-client/references/integration-nextjs.md (Tokens: ~2013 | Category: anthropic-skill) -->\n# Apollo Client Integration with Next.js App Router\n\nThis guide covers integrating Apollo Client in a Next.js application using the App Router architecture with support for both React Server Components (RSC) and Client Components.\n\n## What is supported?\n\n### React Server Components\n\nApollo Client provides a shared client instance across all server components for a single request, preventing duplicate GraphQL requests and optimizing server-side rendering.\n\n### React Client Components\n\nWhen using the `app` directory, client components are rendered both on the server (SSR) and in the browser. Apollo Client enables you to execute GraphQL queries on the server and use the results to hydrate your browser-side cache, delivering fully-rendered pages to users.\n\n## Installation\n\nInstall Apollo Client and the Next.js integration package:\n\n```bash\nnpm install @apollo/client@latest @apollo/client-integration-nextjs graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup for React Server Components (RSC)\n\n### Step 1: Create Apollo Client Configuration\n\nCreate an `ApolloClient.ts` file in your app directory:\n\n```typescript\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  registerApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nexport const { getClient, query, PreloadQuery } = registerApolloClient(() => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({\n      // Use an absolute URL for SSR (relative URLs cannot be used in SSR)\n      uri: \"https://your-api.com/graphql\",\n      fetchOptions: {\n        // Optional: Next.js-specific fetch options for caching and revalidation\n        // See: https://nextjs.org/docs/app/api-reference/functions/fetch\n      },\n    }),\n  });\n});\n```\n\n### Step 2: Use in Server Components\n\nYou can now use the `getClient` function or the `query` shortcut in your server components:\n\n```typescript\nimport { query } from \"./ApolloClient\";\n\nasync function UserProfile({ userId }: { userId: string }) {\n  const { data } = await query({\n    query: GET_USER,\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n### Override Next.js Fetch Options\n\nYou can override Next.js-specific `fetch` options per query using `context.fetchOptions`:\n\n```typescript\nconst { data } = await getClient().query({\n  query: GET_USER,\n  context: {\n    fetchOptions: {\n      next: { revalidate: 60 }, // Revalidate every 60 seconds\n    },\n  },\n});\n```\n\n## Setup for Client Components (SSR and Browser)\n\n### Step 1: Create Apollo Wrapper Component\n\nCreate `app/ApolloWrapper.tsx`:\n\n```typescript\n\"use client\";\n\nimport { HttpLink } from \"@apollo/client\";\nimport {\n  ApolloNextAppProvider,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-nextjs\";\n\nfunction makeClient() {\n  const httpLink = new HttpLink({\n    // Use an absolute URL for SSR\n    uri: \"https://your-api.com/graphql\",\n    fetchOptions: {\n      // Optional: Next.js-specific fetch options\n      // Note: This doesn't work with `export const dynamic = \"force-static\"`\n    },\n  });\n\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: httpLink,\n  });\n}\n\nexport function ApolloWrapper({ children }: React.PropsWithChildren) {\n  return (\n    <ApolloNextAppProvider makeClient={makeClient}>\n      {children}\n    </ApolloNextAppProvider>\n  );\n}\n```\n\n### Step 2: Wrap Root Layout\n\nWrap your `RootLayout` in the `ApolloWrapper` component in `app/layout.tsx`:\n\n```typescript\nimport { ApolloWrapper } from \"./ApolloWrapper\";\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <ApolloWrapper>{children}</ApolloWrapper>\n      </body>\n    </html>\n  );\n}\n```\n\n> **Note:** This works even if your layout is a React Server Component. It ensures all Client Components share the same Apollo Client instance through `ApolloNextAppProvider`.\n\n### Step 3: Use Apollo Client Hooks in Client Components\n\nFor optimal streaming SSR, use suspense-enabled hooks like `useSuspenseQuery` and `useFragment`:\n\n```typescript\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Preloading Data from RSC to Client Components\n\nYou can preload data in React Server Components to populate the cache of your Client Components.\n\n### Step 1: Use PreloadQuery in Server Components\n\n```tsx\nimport { PreloadQuery } from \"./ApolloClient\";\nimport { Suspense } from \"react\";\n\nexport default async function Page() {\n  return (\n    <PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n      <Suspense fallback={<>Loading...</>}>\n        <ClientChild />\n      </Suspense>\n    </PreloadQuery>\n  );\n}\n```\n\n### Step 2: Consume with useSuspenseQuery in Client Components\n\n```tsx\n\"use client\";\n\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nexport function ClientChild() {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n> **Important:** Data fetched this way should be considered client data and never referenced in Server Components. `PreloadQuery` prevents mixing server data and client data by creating a separate `ApolloClient` instance.\n\n### Using with useReadQuery\n\nFor advanced use cases, you can use `PreloadQuery` with `useReadQuery` to avoid request waterfalls:\n\n```tsx\n<PreloadQuery query={GET_USER} variables={{ id: \"1\" }}>\n  {(queryRef) => (\n    <Suspense fallback={<>Loading...</>}>\n      <ClientChild queryRef={queryRef} />\n    </Suspense>\n  )}\n</PreloadQuery>\n```\n\nIn your Client Component:\n\n```tsx\n\"use client\";\n\nimport {\n  useQueryRefHandlers,\n  useReadQuery,\n  QueryRef,\n} from \"@apollo/client/react\";\n\nexport function ClientChild({ queryRef }: { queryRef: QueryRef<TQueryData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.user.name}</div>;\n}\n```\n\n## Handling Multipart Responses (@defer) in SSR\n\nWhen using the `@defer` directive, `useSuspenseQuery` will only suspend until the initial response is received. To handle deferred data properly, you have three strategies:\n\n### Strategy 1: Use PreloadQuery with useReadQuery\n\n`PreloadQuery` allows deferred data to be fully transported and streamed chunk-by-chunk.\n\n### Strategy 2: Remove @defer Fragments\n\nUse `RemoveMultipartDirectivesLink` to strip `@defer` directives from queries during SSR:\n\n```typescript\nimport { RemoveMultipartDirectivesLink } from \"@apollo/client-integration-nextjs\";\n\nnew RemoveMultipartDirectivesLink({\n  stripDefer: true, // Default: true\n});\n```\n\nYou can exclude specific fragments from stripping by labeling them:\n\n```graphql\nquery myQuery {\n  fastField\n  ... @defer(label: \"SsrDontStrip1\") {\n    slowField1\n  }\n}\n```\n\n### Strategy 3: Wait for Deferred Data\n\nUse `AccumulateMultipartResponsesLink` to debounce the initial response:\n\n```typescript\nimport { AccumulateMultipartResponsesLink } from \"@apollo/client-integration-nextjs\";\n\nnew AccumulateMultipartResponsesLink({\n  cutoffDelay: 100, // Wait up to 100ms for incremental data\n});\n```\n\n### Combined Approach: SSRMultipartLink\n\nCombine both strategies with `SSRMultipartLink`:\n\n```typescript\nimport { SSRMultipartLink } from \"@apollo/client-integration-nextjs\";\n\nnew SSRMultipartLink({\n  stripDefer: true,\n  cutoffDelay: 100,\n});\n```\n\n## Testing\n\nReset singleton instances between tests using the `resetApolloClientSingletons` helper:\n\n```typescript\nimport { resetApolloClientSingletons } from \"@apollo/client-integration-nextjs\";\n\nafterEach(resetApolloClientSingletons);\n```\n\n## Debugging\n\nEnable verbose logging in your `app/ApolloWrapper.tsx`:\n\n```typescript\nimport { setLogVerbosity } from \"@apollo/client\";\n\nsetLogVerbosity(\"debug\");\n```\n\n## Important Considerations\n\n1. **Separate RSC and SSR Queries:** Avoid overlapping queries between RSC and SSR. RSC queries don't update in the browser, while SSR queries can update dynamically as the cache changes.\n\n2. **Use Absolute URLs:** Always use absolute URLs in `HttpLink` for SSR, as relative URLs cannot be used in server-side rendering.\n\n3. **Streaming SSR:** For optimal performance, use `useSuspenseQuery` and `useFragment` to take advantage of React 18's streaming SSR capabilities.\n\n4. **Suspense Boundaries:** Place `Suspense` boundaries at meaningful places in your UI for the best user experience.\n\n<!-- File: docs/agent-skills/apollo-client/references/integration-react-router.md (Tokens: ~1684 | Category: anthropic-skill) -->\n# Apollo Client Integration with React Router Framework Mode\n\nThis guide covers integrating Apollo Client in a React Router 7 application with support for modern streaming SSR.\n\n## Installation\n\nInstall Apollo Client and the React Router integration package:\n\n```bash\nnpm install @apollo/client-integration-react-router @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Create Apollo Configuration\n\nCreate an `app/apollo.ts` file that exports a `makeClient` function and an `apolloLoader`:\n\n```typescript\nimport { HttpLink, InMemoryCache } from \"@apollo/client\";\nimport {\n  createApolloLoaderHandler,\n  ApolloClient,\n} from \"@apollo/client-integration-react-router\";\n\n// `request` will be available on the server during SSR or in loaders, but not in the browser\nexport const makeClient = (request?: Request) => {\n  return new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n};\n\nexport const apolloLoader = createApolloLoaderHandler(makeClient);\n```\n\n> **Important:** `ApolloClient` must be imported from `@apollo/client-integration-react-router`, not from `@apollo/client`.\n\n### Step 2: Reveal Entry Files\n\nRun the following command to create the entry files if they don't exist:\n\n```bash\nnpx react-router reveal\n```\n\nThis will create `app/entry.client.tsx` and `app/entry.server.tsx`.\n\n### Step 3: Configure Client Entry\n\nAdjust `app/entry.client.tsx` to wrap your app in `ApolloProvider`:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\nimport { StrictMode, startTransition } from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { HydratedRouter } from \"react-router/dom\";\n\nstartTransition(() => {\n  const client = makeClient();\n  hydrateRoot(\n    document,\n    <StrictMode>\n      <ApolloProvider client={client}>\n        <HydratedRouter />\n      </ApolloProvider>\n    </StrictMode>\n  );\n});\n```\n\n### Step 4: Configure Server Entry\n\nAdjust `app/entry.server.tsx` to wrap your app in `ApolloProvider` during SSR:\n\n```typescript\nimport { makeClient } from \"./apollo\";\nimport { ApolloProvider } from \"@apollo/client\";\n// ... other imports\n\nexport default function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  routerContext: EntryContext\n) {\n  return new Promise((resolve, reject) => {\n    // ... existing code\n\n    const client = makeClient(request);\n\n    const { pipe, abort } = renderToPipeableStream(\n      <ApolloProvider client={client}>\n        <ServerRouter\n          context={routerContext}\n          url={request.url}\n          abortDelay={ABORT_DELAY}\n        />\n      </ApolloProvider>,\n      {\n        [readyOption]() {\n          shellRendered = true;\n          // ... rest of the handler\n        },\n        // ... other options\n      }\n    );\n  });\n}\n```\n\n### Step 5: Add Hydration Helper\n\nAdd `<ApolloHydrationHelper>` to `app/root.tsx`:\n\n```typescript\nimport { ApolloHydrationHelper } from \"@apollo/client-integration-react-router\";\nimport { Links, Meta, Outlet, Scripts, ScrollRestoration } from \"react-router\";\n\nexport function Layout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <Meta />\n        <Links />\n      </head>\n      <body>\n        <ApolloHydrationHelper>{children}</ApolloHydrationHelper>\n        <ScrollRestoration />\n        <Scripts />\n      </body>\n    </html>\n  );\n}\n\nexport default function App() {\n  return <Outlet />;\n}\n```\n\n## Usage\n\n### Using apolloLoader with useReadQuery\n\nYou can now use the `apolloLoader` function to create Apollo-enabled loaders for your routes:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef } = useLoaderData<typeof loader>();\n  const { data } = useReadQuery(userQueryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n> **Important:** To provide better TypeScript support, `apolloLoader` is a method that you need to call twice: `apolloLoader<LoaderArgs>()(loader)`\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { useLoaderData } from \"react-router\";\nimport type { Route } from \"./+types/my-route\";\nimport { apolloLoader } from \"./apollo\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {\n  const userQueryRef = preloadQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const postsQueryRef = preloadQuery(GET_POSTS, {\n    variables: { userId: \"1\" },\n  });\n\n  return {\n    userQueryRef,\n    postsQueryRef,\n  };\n});\n\nexport default function UserPage() {\n  const { userQueryRef, postsQueryRef } = useLoaderData<typeof loader>();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: postsData } = useReadQuery(postsQueryRef);\n\n  return (\n    <div>\n      <h1>{userData.user.name}</h1>\n      <h2>Posts</h2>\n      <ul>\n        {postsData.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import ApolloClient from Integration Package:** Always import `ApolloClient` from `@apollo/client-integration-react-router`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **TypeScript Support:** The `apolloLoader` function requires double invocation for proper TypeScript type inference: `apolloLoader<LoaderArgs>()(loader)`.\n\n3. **Request Context:** The `makeClient` function receives the `Request` object during SSR and in loaders, but not in the browser. Use this to set up auth headers or other request-specific configuration.\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Hydration:** The `ApolloHydrationHelper` component ensures that data loaded on the server is properly hydrated on the client, preventing unnecessary refetches.\n\n<!-- File: docs/agent-skills/apollo-client/references/integration-tanstack-start.md (Tokens: ~2581 | Category: anthropic-skill) -->\n# Apollo Client Integration with TanStack Start\n\nThis guide covers integrating Apollo Client in a TanStack Start application with support for modern streaming SSR.\n\n> **Note:** When using `npx create-tsrouter-app` to create a new TanStack Start application, you can choose Apollo Client in the setup wizard to have all of this configuration automatically set up for you.\n\n## Installation\n\nInstall Apollo Client and the TanStack Start integration package:\n\n```bash\nnpm install @apollo/client-integration-tanstack-start @apollo/client graphql rxjs\n```\n\n> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).\n\n## Setup\n\n### Step 1: Configure Root Route with Context\n\nIn your `routes/__root.tsx`, change from `createRootRoute` to `createRootRouteWithContext` to provide the right context type:\n\n```typescript\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\nimport {\n  createRootRouteWithContext,\n  Outlet,\n} from \"@tanstack/react-router\";\n\nexport const Route = createRootRouteWithContext<ApolloClientIntegration.RouterContext>()({\n  component: RootComponent,\n});\n\nfunction RootComponent() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Outlet />\n      </body>\n    </html>\n  );\n}\n```\n\n### Step 2: Set Up Apollo Client in Router\n\nIn your `router.tsx`, set up your Apollo Client instance and run `routerWithApolloClient`:\n\n```typescript\nimport {\n  routerWithApolloClient,\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** `ApolloClient` and `InMemoryCache` must be imported from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`.\n\n## Usage\n\n### Option 1: Loader with preloadQuery and useReadQuery\n\nUse the `preloadQuery` function in your route loader to preload data during navigation:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_USER: TypedDocumentNode<\n  { user: { id: string; name: string; email: string } },\n  { id: string }\n> = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/user/$userId\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery }, params }) => {\n    const queryRef = preloadQuery(GET_USER, {\n      variables: { id: params.userId },\n    });\n\n    return {\n      queryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { queryRef } = Route.useLoaderData();\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <p>{data.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Option 2: Direct useSuspenseQuery in Component\n\nYou can also use Apollo Client's suspenseful hooks directly in your component without a loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\nimport type { TypedDocumentNode } from \"@apollo/client\";\n\n// TypedDocumentNode definition with types\nconst GET_POSTS: TypedDocumentNode<{\n  posts: Array<{ id: string; title: string; content: string }>;\n}> = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n      content\n    }\n  }\n`;\n\nexport const Route = createFileRoute(\"/posts\")({\n  component: RouteComponent,\n});\n\nfunction RouteComponent() {\n  const { data } = useSuspenseQuery(GET_POSTS);\n\n  return (\n    <div>\n      <h1>Posts</h1>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>\n            <h2>{post.title}</h2>\n            <p>{post.content}</p>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### Multiple Queries in a Loader\n\nYou can preload multiple queries in a single loader:\n\n```typescript\nimport { gql } from \"@apollo/client\";\nimport { useReadQuery } from \"@apollo/client/react\";\nimport { createFileRoute } from \"@tanstack/react-router\";\n\n// TypedDocumentNode definitions omitted for brevity\n\nexport const Route = createFileRoute(\"/dashboard\")({\n  component: RouteComponent,\n  loader: ({ context: { preloadQuery } }) => {\n    const userQueryRef = preloadQuery(GET_USER, {\n      variables: { id: \"current\" },\n    });\n\n    const statsQueryRef = preloadQuery(GET_STATS, {\n      variables: { period: \"month\" },\n    });\n\n    return {\n      userQueryRef,\n      statsQueryRef,\n    };\n  },\n});\n\nfunction RouteComponent() {\n  const { userQueryRef, statsQueryRef } = Route.useLoaderData();\n  const { data: userData } = useReadQuery(userQueryRef);\n  const { data: statsData } = useReadQuery(statsQueryRef);\n\n  return (\n    <div>\n      <h1>Welcome, {userData.user.name}</h1>\n      <div>\n        <h2>Monthly Stats</h2>\n        <p>Views: {statsData.stats.views}</p>\n        <p>Clicks: {statsData.stats.clicks}</p>\n      </div>\n    </div>\n  );\n}\n```\n\n### Using useQueryRefHandlers for Refetching\n\nWhen using `useReadQuery`, you can get refetch functionality from `useQueryRefHandlers`:\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n```typescript\nimport { useReadQuery, useQueryRefHandlers, QueryRef } from \"@apollo/client/react\";\n\nfunction UserComponent({ queryRef }: { queryRef: QueryRef<GetUserQuery> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.user.name}</h1>\n      <button onClick={() => refetch()}>Refresh</button>\n    </div>\n  );\n}\n```\n\n## Important Considerations\n\n1. **Import from Integration Package:** Always import `ApolloClient` and `InMemoryCache` from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`, to ensure proper SSR hydration.\n\n2. **Context Type:** Use `createRootRouteWithContext<ApolloClientIntegration.RouterContext>()` to provide proper TypeScript types for the `preloadQuery` function in loaders.\n\n3. **Loader vs Component Queries:**\n\n   - Use `preloadQuery` in loaders when you want to start fetching data before the component renders\n   - Use `useSuspenseQuery` directly in components for simpler cases or when data fetching can wait until render\n\n4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.\n\n5. **Cache Management:** The Apollo Client instance is shared across all routes, so cache updates from one route will be reflected in all routes that use the same data.\n\n6. **Authentication:** Use Apollo Client's `SetContextLink` for dynamic auth tokens.\n\n## Advanced Configuration\n\n### Adding Authentication\n\nFor authentication in TanStack Start with SSR support, you need to handle both server and client environments differently. Use `createIsomorphicFn` to provide environment-specific implementations:\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n  routerWithApolloClient,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { ApolloLink, HttpLink } from \"@apollo/client\";\nimport { SetContextLink } from \"@apollo/client/link/context\";\nimport { createIsomorphicFn } from \"@tanstack/react-start\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { getSession, getCookie } from \"@tanstack/react-start/server\";\nimport { routeTree } from \"./routeTree.gen\";\n\n// Create isomorphic link that uses different implementations per environment\nconst createAuthLink = createIsomorphicFn()\n  .server(() => {\n    // Server-only: Can access server-side functions like `getCookies`, `getCookie`, `getSession`, etc. exported from `\"@tanstack/react-start/server\"`\n    return new SetContextLink(async (prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: getCookie(\"Authorization\"),\n        },\n      };\n    });\n  })\n  .client(() => {\n    // Client-only: Can access `localStorage` or other browser APIs\n    return new SetContextLink((prevContext) => {\n      return {\n        headers: {\n          ...prevContext.headers,\n          authorization: localStorage.getItem(\"authToken\") ?? \"\",\n        },\n      };\n    });\n  });\n\nexport function getRouter() {\n  const httpLink = new HttpLink({\n    uri: \"https://your-graphql-endpoint.com/graphql\",\n  });\n\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache(),\n    link: ApolloLink.from([createAuthLink(), httpLink]),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n> **Important:** The `getRouter` function is called both on the server and client, so it must not contain environment-specific code. Use `createIsomorphicFn` to provide different implementations:\n>\n> - **Server:** Can access server-only functions like `getSession`, `getCookies`, `getCookie` from `@tanstack/react-start/server` to access authentication information in request or session data\n> - **Client:** Can use `localStorage` or other browser APIs to access auth tokens (if setting `credentials: \"include\"` is sufficient, try to prefer that over manually setting auth headers client-side)\n>\n> This ensures your authentication works correctly in both SSR and browser contexts.\n\n### Custom Cache Configuration\n\n```typescript\nimport {\n  ApolloClient,\n  InMemoryCache,\n} from \"@apollo/client-integration-tanstack-start\";\nimport { HttpLink } from \"@apollo/client\";\nimport { createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\nimport { routerWithApolloClient } from \"@apollo/client-integration-tanstack-start\";\n\nexport function getRouter() {\n  const apolloClient = new ApolloClient({\n    cache: new InMemoryCache({\n      typePolicies: {\n        Query: {\n          fields: {\n            posts: {\n              merge(existing = [], incoming) {\n                return [...existing, ...incoming];\n              },\n            },\n          },\n        },\n      },\n    }),\n    link: new HttpLink({ uri: \"https://your-graphql-endpoint.com/graphql\" }),\n  });\n\n  const router = createRouter({\n    routeTree,\n    context: {\n      ...routerWithApolloClient.defaultContext,\n    },\n  });\n\n  return routerWithApolloClient(router, apolloClient);\n}\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/mutations.md (Tokens: ~3001 | Category: anthropic-skill) -->\n# Mutations Reference\n\n## Table of Contents\n\n- [useMutation Hook](#usemutation-hook)\n- [Mutation Variables](#mutation-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [Optimistic UI](#optimistic-ui)\n- [Cache Updates](#cache-updates)\n- [Refetch Queries](#refetch-queries)\n- [Error Handling](#error-handling)\n\n## useMutation Hook\n\nThe `useMutation` hook is used to execute GraphQL mutations.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst ADD_TODO = gql`\n  mutation AddTodo($text: String!) {\n    addTodo(text: $text) {\n      id\n      text\n      completed\n    }\n  }\n`;\n\nfunction AddTodo() {\n  const [addTodo, { data, loading, error }] = useMutation(ADD_TODO);\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        const form = e.currentTarget;\n        const text = new FormData(form).get(\"text\") as string;\n        addTodo({ variables: { text } });\n        form.reset();\n      }}\n    >\n      <input name=\"text\" placeholder=\"Add todo\" />\n      <button type=\"submit\" disabled={loading}>\n        Add\n      </button>\n      {error && <p>Error: {error.message}</p>}\n    </form>\n  );\n}\n```\n\n### Return Tuple\n\n```typescript\nconst [\n  mutateFunction, // Function to call to execute mutation\n  {\n    data, // Mutation result data\n    loading, // True while mutation is in flight\n    error, // ApolloError if mutation failed\n    called, // True if mutation has been called\n    reset, // Reset mutation state\n    client, // Apollo Client instance\n  },\n] = useMutation(MUTATION);\n```\n\n## Mutation Variables\n\n### Variables in Options\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  variables: {\n    input: {\n      name: \"Default User\",\n      email: \"default@example.com\",\n    },\n  },\n});\n\n// Call with default variables\nawait createUser();\n\n// Override variables\nawait createUser({\n  variables: {\n    input: {\n      name: \"Custom User\",\n      email: \"custom@example.com\",\n    },\n  },\n});\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\n\ninterface CreateUserData {\n  createUser: {\n    id: string;\n    name: string;\n    email: string;\n  };\n}\n\ninterface CreateUserVariables {\n  input: {\n    name: string;\n    email: string;\n  };\n}\n\nconst CREATE_USER: TypedDocumentNode<CreateUserData, CreateUserVariables> = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nconst [createUser, { data, loading }] = useMutation(CREATE_USER);\n\nconst { data } = await createUser({\n  variables: {\n    input: { name: \"John\", email: \"john@example.com\" },\n  },\n});\n\n// data.createUser is fully typed\n```\n\n## Loading and Error States\n\n### Handling in UI\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading, error, data, reset }] =\n    useMutation(CREATE_POST);\n\n  if (data) {\n    return (\n      <div>\n        <p>Post created: {data.createPost.title}</p>\n        <button onClick={reset}>Create another</button>\n      </div>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <input name=\"title\" disabled={loading} />\n      <textarea name=\"content\" disabled={loading} />\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n      {error && (\n        <div className=\"error\">\n          <p>Failed to create post: {error.message}</p>\n          <button onClick={reset}>Try again</button>\n        </div>\n      )}\n    </form>\n  );\n}\n```\n\n### Async/Await Pattern\n\nIf you only need the promise without using the hook's loading/data state, use `client.mutate` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction CreatePost() {\n  const client = useApolloClient();\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await client.mutate({\n        mutation: CREATE_POST,\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      ...\n    </form>\n  );\n}\n```\n\nIf you do use the hook's state, e.g. because you want to render the `loading` state, errors or returned `data`, you can also use the `useMutation` hook with `async..await` in your handler:\n\n```tsx\nfunction CreatePost() {\n  const [createPost, { loading }] = useMutation(CREATE_POST);\n\n  async function handleSubmit(formData: FormData) {\n    try {\n      const { data } = await createPost({\n        variables: {\n          input: {\n            title: formData.get(\"title\"),\n            content: formData.get(\"content\"),\n          },\n        },\n      });\n      console.log(\"Created:\", data.createPost);\n      router.push(`/posts/${data.createPost.id}`);\n    } catch (error) {\n      console.error(\"Failed to create post:\", error);\n    }\n  }\n\n  return (\n    <form\n      onSubmit={(e) => {\n        e.preventDefault();\n        handleSubmit(new FormData(e.currentTarget));\n      }}\n    >\n      <button type=\"submit\" disabled={loading}>\n        {loading ? \"Creating...\" : \"Create Post\"}\n      </button>\n    </form>\n  );\n}\n```\n\n## Optimistic UI\n\nOptimistic UI immediately reflects the expected result of a mutation before the server responds.\n\n### Basic Optimistic Response\n\n**Important**: `optimisticResponse` needs to be a full valid response for the mutation. A partial result might result in subtle errors.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  optimisticResponse: {\n    addTodo: {\n      __typename: \"Todo\",\n      id: \"temp-id\",\n      text: \"New todo\",\n      completed: false,\n    },\n  },\n});\n```\n\n### Dynamic Optimistic Response\n\n```tsx\nfunction TodoList() {\n  const [addTodo] = useMutation(ADD_TODO);\n\n  const handleAdd = (text: string) => {\n    addTodo({\n      variables: { text },\n      optimisticResponse: {\n        addTodo: {\n          __typename: \"Todo\",\n          id: `temp-${Date.now()}`,\n          text,\n          completed: false,\n        },\n      },\n    });\n  };\n\n  return <AddTodoForm onAdd={handleAdd} />;\n}\n```\n\n### Optimistic Response with Cache Update\n\n```tsx\nconst [toggleTodo] = useMutation(TOGGLE_TODO, {\n  optimisticResponse: ({ id }) => ({\n    toggleTodo: {\n      __typename: \"Todo\",\n      id,\n      completed: true, // Assume success\n    },\n  }),\n  update: (cache, { data }) => {\n    // This runs twice: once with optimistic data, once with server data\n    cache.modify({\n      id: cache.identify(data.toggleTodo),\n      fields: {\n        completed: () => data.toggleTodo.completed,\n      },\n    });\n  },\n});\n```\n\n## Cache Updates\n\n### Using update Function\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Read existing todos from cache\n    const existingTodos = cache.readQuery<{ todos: Todo[] }>({\n      query: GET_TODOS,\n    });\n\n    // Write updated list back to cache\n    cache.writeQuery({\n      query: GET_TODOS,\n      data: {\n        todos: [...(existingTodos?.todos ?? []), data.addTodo],\n      },\n    });\n  },\n});\n```\n\n### cache.modify\n\n```tsx\nconst [deleteTodo] = useMutation(DELETE_TODO, {\n  update: (cache, { data }) => {\n    cache.modify({\n      fields: {\n        todos: (existingTodos: Reference[], { readField }) => {\n          return existingTodos.filter(\n            (todoRef) => readField(\"id\", todoRef) !== data.deleteTodo.id\n          );\n        },\n      },\n    });\n  },\n});\n```\n\n### cache.evict\n\n```tsx\nconst [deleteUser] = useMutation(DELETE_USER, {\n  update: (cache, { data }) => {\n    // Remove the user object from cache entirely\n    cache.evict({ id: cache.identify(data.deleteUser) });\n    // Clean up dangling references\n    cache.gc();\n  },\n});\n```\n\n### Updating Related Queries\n\n```tsx\nconst [createPost] = useMutation(CREATE_POST, {\n  update: (cache, { data }) => {\n    // Update author's post count\n    cache.modify({\n      id: cache.identify({ __typename: \"User\", id: data.createPost.authorId }),\n      fields: {\n        postCount: (existing) => existing + 1,\n        posts: (existing, { toReference }) => [\n          ...existing,\n          toReference(data.createPost),\n        ],\n      },\n    });\n\n    // Add to feed\n    cache.modify({\n      fields: {\n        feed: (existing, { toReference }) => [\n          toReference(data.createPost),\n          ...existing,\n        ],\n      },\n    });\n  },\n});\n```\n\n## Refetch Queries\n\n### Basic Refetch\n\nThere are three refetch notations:\n\n- **String**: `refetchQueries: ['getTodos']` - refetches all active `getTodos` queries\n- **Query document**: `refetchQueries: [GET_TODOS]` - refetches all active queries using this document\n- **Object**: `refetchQueries: [{ query: GET_TODOS }, { query: GET_TODOS, variables: { page: 25 } }]` - **fetches** the query, regardless if it's actively used in the UI\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  // Refetch all active GET_TODOS queries\n  refetchQueries: [\"getTodos\"],\n  // Or: refetchQueries: [GET_TODOS],\n});\n\n// Fetch specific query with variables (even if not active)\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }, { query: GET_TODO_COUNT }],\n});\n```\n\n### Conditional Refetch\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: (result) => {\n    if (result.data?.addTodo.priority === \"HIGH\") {\n      return [{ query: GET_HIGH_PRIORITY_TODOS }];\n    }\n    return [{ query: GET_TODOS }];\n  },\n});\n```\n\n### Refetch Active Queries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: \"active\", // Refetch all active queries\n  // Or: 'all' to refetch all queries (including inactive)\n});\n```\n\n### awaitRefetchQueries\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  refetchQueries: [{ query: GET_TODOS }],\n  awaitRefetchQueries: true, // Wait for refetch before resolving mutation\n});\n```\n\n### onQueryUpdated\n\nReturning `true` from `onQueryUpdated` causes a refetch. Don't call `refetch()` manually inside `onQueryUpdated`, as it won't retain the query and might cancel it early.\n\n```tsx\nconst [addTodo] = useMutation(ADD_TODO, {\n  update: (cache, { data }) => {\n    // Update cache...\n  },\n  onQueryUpdated: (observableQuery) => {\n    // Called for each query affected by cache update\n    console.log(`Query ${observableQuery.queryName} was updated`);\n    // Return true to refetch\n    return true;\n  },\n});\n```\n\n## Error Handling\n\n### Error Policy\n\n```tsx\nconst [createUser, { loading }] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\", // Return both data and errors\n});\n\nconst { data, errors } = await createUser({\n  variables: { input },\n});\n\n// Handle partial success\nif (data?.createUser) {\n  console.log(\"User created:\", data.createUser);\n}\nif (errors) {\n  console.warn(\"Some errors occurred:\", errors);\n}\n```\n\n### onError Callback\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  onError: (error) => {\n    // Handle error globally\n    toast.error(`Failed to create user: ${error.message}`);\n\n    // Log to error tracking service\n    Sentry.captureException(error);\n  },\n  onCompleted: (data) => {\n    toast.success(`User ${data.createUser.name} created!`);\n  },\n});\n```\n\n### Field-Level Errors\n\n```tsx\nconst [createUser] = useMutation(CREATE_USER, {\n  errorPolicy: \"all\",\n});\n\nconst handleSubmit = async (input: CreateUserInput) => {\n  const { data, errors } = await createUser({\n    variables: { input },\n  });\n\n  // Handle GraphQL validation errors\n  const fieldErrors = errors?.reduce(\n    (acc, error) => {\n      const field = error.extensions?.field as string;\n      if (field) {\n        acc[field] = error.message;\n      }\n      return acc;\n    },\n    {} as Record<string, string>\n  );\n\n  if (fieldErrors?.email) {\n    setEmailError(fieldErrors.email);\n  }\n};\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/queries.md (Tokens: ~2641 | Category: anthropic-skill) -->\n# Queries Reference\n\n> **Note**: In most applications, there should only be one use of a query hook per page. Use fragment-reading hooks (`useFragment`, `useSuspenseFragment`) with component-colocated fragments and data masking for the rest of the page components.\n\n## Table of Contents\n\n- [useQuery Hook](#usequery-hook)\n- [Query Variables](#query-variables)\n- [Loading and Error States](#loading-and-error-states)\n- [useLazyQuery](#uselazyquery)\n- [Polling and Refetching](#polling-and-refetching)\n- [Fetch Policies](#fetch-policies)\n- [Conditional Queries](#conditional-queries)\n\n## useQuery Hook\n\nThe `useQuery` hook is the primary way to fetch data in Apollo Client in non-suspenseful applications. It returns loading and error states that must be handled.\n\n> **Note**: In suspenseful applications, use `useSuspenseQuery` or `useBackgroundQuery` instead. See the [Suspense Hooks reference](suspense-hooks.md) for more details.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_DOGS = gql`\n  query GetDogs {\n    dogs {\n      id\n      breed\n      displayImage\n    }\n  }\n`;\n\nfunction Dogs() {\n  const { loading, error, data } = useQuery(GET_DOGS);\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  loading, // True during initial load\n  error, // ApolloError if request failed\n  networkStatus, // Detailed network state (1-8)\n  dataState, // For TypeScript type narrowing (AC 4.x)\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n  startPolling, // Start polling at interval\n  stopPolling, // Stop polling\n  subscribeToMore, // Add subscription to query\n  updateQuery, // Manually update query result\n  client, // Apollo Client instance\n  called, // True if query has been executed\n  previousData, // Previous data (useful during loading)\n} = useQuery(QUERY);\n```\n\n## Query Variables\n\n### Basic Variables\n\n```tsx\nconst GET_DOG = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DogPhoto({ breed }: { breed: string }) {\n  const { loading, error, data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  if (loading) return null;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return <img src={data.dog.displayImage} alt={breed} />;\n}\n```\n\n### TypeScript Types\n\nUse `TypedDocumentNode` instead of generic type parameters for better type safety:\n\n```typescript\nimport { gql, TypedDocumentNode } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\ninterface GetDogData {\n  dog: {\n    id: string;\n    displayImage: string;\n  };\n}\n\ninterface GetDogVariables {\n  breed: string;\n}\n\nconst GET_DOG: TypedDocumentNode<GetDogData, GetDogVariables> = gql`\n  query GetDog($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_DOG, {\n  variables: { breed: \"bulldog\" },\n});\n\n// data?.dog is fully typed\n```\n\n### Dynamic Variables\n\n```tsx\nfunction DogSelector() {\n  const [breed, setBreed] = useState(\"bulldog\");\n\n  // Query automatically re-runs when breed changes\n  const { data } = useQuery(GET_DOG, {\n    variables: { breed },\n  });\n\n  return (\n    <select value={breed} onChange={(e) => setBreed(e.target.value)}>\n      <option value=\"bulldog\">Bulldog</option>\n      <option value=\"poodle\">Poodle</option>\n    </select>\n  );\n}\n```\n\n## Loading and Error States\n\n### Using Previous Data\n\n```tsx\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, data, previousData } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  // Show previous data while loading new data\n  const displayData = data ?? previousData;\n\n  return (\n    <div>\n      {loading && <LoadingSpinner />}\n      {displayData && <UserCard user={displayData.user} />}\n    </div>\n  );\n}\n```\n\n### Network Status\n\n```tsx\nimport { NetworkStatus } from \"@apollo/client\";\n\nfunction Dogs() {\n  const { loading, error, data, networkStatus, refetch } = useQuery(GET_DOGS, {\n    notifyOnNetworkStatusChange: true,\n  });\n\n  if (networkStatus === NetworkStatus.refetch) {\n    return <p>Refetching...</p>;\n  }\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <>\n      <button onClick={() => refetch()}>Refresh</button>\n      <ul>\n        {data.dogs.map((dog) => (\n          <li key={dog.id}>{dog.breed}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n## useLazyQuery\n\nUse `useLazyQuery` when you want to execute a query in response to a user-triggered event (like a button click) rather than on component mount.\n\n**Important**: `useLazyQuery` doesn't guarantee a network request - it only sets variables. If data is already in the cache, this isn't a \"refetch\". Only use `useLazyQuery` if you consume the second tuple value (loading, data, error states) to synchronize cache data with the component. If you only need the promise, use `client.query` directly instead.\n\n### Basic Usage\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useLazyQuery } from \"@apollo/client/react\";\n\nconst GET_DOG_PHOTO = gql`\n  query GetDogPhoto($breed: String!) {\n    dog(breed: $breed) {\n      id\n      displayImage\n    }\n  }\n`;\n\nfunction DelayedQuery() {\n  const [getDog, { loading, error, data, called }] =\n    useLazyQuery(GET_DOG_PHOTO);\n\n  if (called && loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  return (\n    <div>\n      {data?.dog && <img src={data.dog.displayImage} />}\n      <button onClick={() => getDog({ variables: { breed: \"bulldog\" } })}>\n        Get Bulldog Photo\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use client.query Instead\n\nIf you only need the promise result and don't consume the loading/error/data states from the hook, use `client.query` instead:\n\n```tsx\nimport { useApolloClient } from \"@apollo/client/react\";\n\nfunction SearchDogs() {\n  const client = useApolloClient();\n  const [search, setSearch] = useState(\"\");\n\n  const handleSearch = async () => {\n    try {\n      const { data } = await client.query({\n        query: SEARCH_DOGS,\n        variables: { query: search },\n      });\n      console.log(\"Found dogs:\", data.searchDogs);\n    } catch (error) {\n      console.error(\"Search failed:\", error);\n    }\n  };\n\n  return (\n    <div>\n      <input value={search} onChange={(e) => setSearch(e.target.value)} />\n      <button onClick={handleSearch}>Search</button>\n    </div>\n  );\n}\n```\n\n## Polling and Refetching\n\n### Polling\n\n```tsx\nfunction LiveFeed() {\n  const { data, startPolling, stopPolling } = useQuery(GET_FEED, {\n    pollInterval: 5000, // Poll every 5 seconds\n  });\n\n  // Or control polling dynamically\n  useEffect(() => {\n    startPolling(5000);\n    return () => stopPolling();\n  }, [startPolling, stopPolling]);\n\n  return <Feed items={data?.feed} />;\n}\n```\n\n### Manual Refetching\n\n```tsx\nfunction DogList() {\n  const { data, refetch } = useQuery(GET_DOGS);\n\n  return (\n    <div>\n      <button onClick={() => refetch()}>Refresh</button>\n      <button onClick={() => refetch({ breed: \"poodle\" })}>\n        Refetch Poodles\n      </button>\n      <ul>{data?.dogs.map((dog) => <li key={dog.id}>{dog.breed}</li>)}</ul>\n    </div>\n  );\n}\n```\n\n## Fetch Policies\n\nControl how the query interacts with the cache.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-only`        | Only return cached data, never fetch                       |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n| `standby`           | Same as cache-first but doesn't auto-update                |\n\n### Usage Examples\n\n```tsx\n// Real-time data - always fetch\nconst { data } = useQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Static data - prefer cache\nconst { data } = useQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data, loading } = useQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n\n// Fetch once, then use cache\nconst { data } = useQuery(GET_USER_PROFILE, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n```\n\n### nextFetchPolicy\n\n```tsx\n// First request: network-only\n// Subsequent requests: cache-first\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: \"cache-first\",\n});\n\n// Or use a function for more control\nconst { data } = useQuery(GET_POSTS, {\n  fetchPolicy: \"network-only\",\n  nextFetchPolicy: (currentFetchPolicy, { reason, observable }) => {\n    if (reason === \"after-fetch\") {\n      return \"cache-first\";\n    }\n    return currentFetchPolicy;\n  },\n});\n```\n\n## Conditional Queries\n\n### Using skipToken (Recommended)\n\nUse `skipToken` to conditionally skip queries without TypeScript issues:\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n### Skip Option (Alternative)\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data } = useQuery(GET_USER, {\n    variables: { id: userId! },\n    skip: !userId, // Don't execute if no userId\n  });\n\n  return userId ? <Profile user={data?.user} /> : <p>Select a user</p>;\n}\n```\n\n> **Note**: Using `skipToken` is preferred over `skip` as it avoids TypeScript issues with required variables and the non-null assertion operator.\n\n### SSR Skip\n\n```tsx\n// Skip during server-side rendering\nconst { data } = useQuery(GET_USER_LOCATION, {\n  skip: typeof window === \"undefined\",\n  ssr: false,\n});\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/state-management.md (Tokens: ~2379 | Category: anthropic-skill) -->\n# State Management Reference\n\n## Table of Contents\n\n- [Reactive Variables](#reactive-variables)\n- [Local-Only Fields](#local-only-fields)\n- [LocalState Resolvers](#localstate-resolvers)\n- [Combining Remote and Local State](#combining-remote-and-local-state)\n- [useReactiveVar Hook](#usereactivevar-hook)\n\n## Reactive Variables\n\nReactive variables are a way to store local state outside of the Apollo Client cache while still triggering reactive updates.\n\n**Important**: Reactive variables store a single value that notifies `ApolloClient` instances when changed. They do not have separate values per ApolloClient instance. In multi-user environments like SSR, global or module-level reactive variables could be shared between users and cause data leaks. In frameworks that use SSR, always avoid storing reactive variables as globals.\n\n### Creating Reactive Variables\n\n```typescript\nimport { makeVar } from \"@apollo/client\";\n\n// Simple reactive variable\nexport const isLoggedInVar = makeVar<boolean>(false);\n\n// Object reactive variable\nexport const cartItemsVar = makeVar<CartItem[]>([]);\n\n// Complex state\ninterface AppState {\n  theme: \"light\" | \"dark\";\n  sidebarOpen: boolean;\n  notifications: Notification[];\n}\n\nexport const appStateVar = makeVar<AppState>({\n  theme: \"light\",\n  sidebarOpen: true,\n  notifications: [],\n});\n```\n\n### Reading Reactive Variables\n\n```tsx\n// Direct read (non-reactive)\nconst isLoggedIn = isLoggedInVar();\n\n// Reactive read in component\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction AuthButton() {\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return isLoggedIn ?\n      <button onClick={() => isLoggedInVar(false)}>Logout</button>\n    : <button onClick={() => isLoggedInVar(true)}>Login</button>;\n}\n```\n\n### Updating Reactive Variables\n\n```typescript\n// Set new value\nisLoggedInVar(true);\n\n// Update based on current value\ncartItemsVar([...cartItemsVar(), newItem]);\n\n// Update object state\nappStateVar({\n  ...appStateVar(),\n  theme: \"dark\",\n});\n\n// Helper function pattern\nexport function toggleSidebar() {\n  const current = appStateVar();\n  appStateVar({ ...current, sidebarOpen: !current.sidebarOpen });\n}\n\nexport function addNotification(notification: Notification) {\n  const current = appStateVar();\n  appStateVar({\n    ...current,\n    notifications: [...current.notifications, notification],\n  });\n}\n```\n\n## Local-Only Fields\n\nLocal-only fields are fields defined in queries but resolved entirely on the client using the `@client` directive.\n\n**Important**: To use any `@client` fields, you need to add `LocalState` to the `ApolloClient` initialization:\n\n```typescript\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({}),\n  // ... other options\n});\n```\n\n> **Note**: `LocalState` is an Apollo Client 4.x concept and did not exist as a class in previous versions. In previous versions, a `localState` option was not necessary, and local resolvers (if used) could be passed directly to the `ApolloClient` constructor.\n\n### Basic @client Fields\n\n```tsx\nconst GET_USER_WITH_LOCAL = gql`\n  query GetUserWithLocal($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n      # Local-only fields\n      isSelected @client\n      displayName @client\n    }\n  }\n`;\n\nfunction UserCard({ userId }: { userId: string }) {\n  const { data } = useQuery(GET_USER_WITH_LOCAL, {\n    variables: { id: userId },\n  });\n\n  return (\n    <div className={data?.user.isSelected ? \"selected\" : \"\"}>\n      <h2>{data?.user.displayName}</h2>\n      <p>{data?.user.email}</p>\n    </div>\n  );\n}\n```\n\n### Local Field Read Functions (Type Policies)\n\nLocal field `read` functions are defined in entity-level type policies. You can use reactive variables inside these `read` functions, along with other calculations or derived values:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    User: {\n      fields: {\n        // Simple local field from reactive variable\n        isSelected: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return selectedUsersVar().includes(id);\n          },\n        },\n\n        // Computed local field (derived value)\n        displayName: {\n          read(_, { readField }) {\n            const name = readField(\"name\");\n            const email = readField(\"email\");\n            return name || email?.split(\"@\")[0] || \"Anonymous\";\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n## LocalState Resolvers\n\n### Query-Level Local Resolvers\n\nQuery-level local fields can be defined using `LocalState` resolvers. **Note**: Do not read reactive variables inside LocalState resolvers - this is not a documented/tested feature. It might not behave as expected.\n\n```typescript\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Query: {\n        // Read from localStorage\n        theme: () => {\n          if (typeof window !== \"undefined\") {\n            return localStorage.getItem(\"theme\") || \"light\";\n          }\n          return \"light\";\n        },\n\n        // Read from cache\n        currentUser: (_, __, { cache }) => {\n          if (typeof window === \"undefined\") return null;\n          const userId = localStorage.getItem(\"currentUserId\");\n          if (!userId) return null;\n          return cache.readFragment({\n            id: cache.identify({ __typename: \"User\", id: userId }),\n            fragment: gql`\n              fragment CurrentUser on User {\n                id\n                name\n                email\n              }\n            `,\n          });\n        },\n\n        // Compute value\n        isOnline: () => {\n          if (typeof navigator !== \"undefined\") {\n            return navigator.onLine;\n          }\n          return true;\n        },\n      },\n    },\n  }),\n});\n```\n\n### Using Local Query Fields\n\n```tsx\nconst GET_AUTH_STATE = gql`\n  query GetAuthState {\n    isLoggedIn @client\n    currentUser @client {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction AuthStatus() {\n  const { data } = useQuery(GET_AUTH_STATE);\n\n  if (!data?.isLoggedIn) {\n    return <LoginButton />;\n  }\n\n  return <UserMenu user={data.currentUser} />;\n}\n```\n\n## Combining Remote and Local State\n\n### Mixing Remote and Local Fields\n\n```tsx\nconst GET_PRODUCTS = gql`\n  query GetProducts {\n    products {\n      id\n      name\n      price\n      # Local fields\n      quantity @client\n      isInCart @client\n    }\n  }\n`;\n\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Product: {\n      fields: {\n        quantity: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            const cartItem = cartItemsVar().find(\n              (item) => item.productId === id\n            );\n            return cartItem?.quantity ?? 0;\n          },\n        },\n\n        isInCart: {\n          read(_, { readField }) {\n            const id = readField(\"id\");\n            return cartItemsVar().some((item) => item.productId === id);\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Local Mutations\n\n```tsx\nimport { LocalState } from \"@apollo/client/local-state\";\n\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  localState: new LocalState({\n    resolvers: {\n      Mutation: {\n        addToCart: (_, { productId, quantity }, { cache }) => {\n          // Read current cart from cache\n          const { cart } = cache.readQuery({ query: GET_CART }) || { cart: [] };\n\n          const existing = cart.find((item) => item.productId === productId);\n\n          const updatedCart =\n            existing ?\n              cart.map((item) =>\n                item.productId === productId ?\n                  { ...item, quantity: item.quantity + quantity }\n                : item\n              )\n            : [...cart, { productId, quantity, __typename: \"CartItem\" }];\n\n          // Write updated cart back to cache\n          cache.writeQuery({\n            query: GET_CART,\n            data: { cart: updatedCart },\n          });\n\n          return true;\n        },\n      },\n    },\n  }),\n});\n\nconst ADD_TO_CART = gql`\n  mutation AddToCart($productId: ID!, $quantity: Int!) {\n    addToCart(productId: $productId, quantity: $quantity) @client\n  }\n`;\n```\n\n### Persisting Local State\n\n```typescript\n// Create a helper function to permanently subscribe to reactive variable changes, without creating memory leaks\nfunction subscribeToVariable<T>(\n  weakRef: WeakRef<ReactiveVar<T>>,\n  listener: ReactiveListener<T>\n) {\n  weakRef.deref()?.onNextChange((value) => {\n    listener(value);\n    subscribeToVariable(weakRef, listener);\n  });\n}\n\n// Create reactive variable with persistence\nconst persistentCartVar = makeVar<CartItem[]>(\n  typeof window !== \"undefined\" && localStorage.getItem(\"cart\") ?\n    JSON.parse(localStorage.getItem(\"cart\")!)\n  : []\n);\n\n// Save to localStorage when reactive variable changes\nsubscribeToVariable(new WeakRef(persistentCartVar), (items) => {\n  try {\n    if (typeof window !== \"undefined\") {\n      localStorage.setItem(\"cart\", JSON.stringify(items));\n    }\n  } catch (error) {\n    console.error(\"Failed to persist cart:\", error);\n  }\n});\n```\n\n## useReactiveVar Hook\n\nThe `useReactiveVar` hook subscribes a component to reactive variable updates.\n\n### Basic Usage\n\n```tsx\nimport { useReactiveVar } from \"@apollo/client/react\";\n\nfunction ThemeToggle() {\n  const theme = useReactiveVar(themeVar);\n\n  return (\n    <button onClick={() => themeVar(theme === \"light\" ? \"dark\" : \"light\")}>\n      Current: {theme}\n    </button>\n  );\n}\n```\n\n### With Derived State\n\n```tsx\nfunction CartSummary() {\n  const cartItems = useReactiveVar(cartItemsVar);\n\n  // Derived values are computed on each render\n  const totalItems = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n  const totalPrice = cartItems.reduce(\n    (sum, item) => sum + item.price * item.quantity,\n    0\n  );\n\n  return (\n    <div>\n      <p>Items: {totalItems}</p>\n      <p>Total: ${totalPrice.toFixed(2)}</p>\n    </div>\n  );\n}\n```\n\n### Multiple Reactive Variables\n\n```tsx\nfunction AppLayout() {\n  const theme = useReactiveVar(themeVar);\n  const sidebarOpen = useReactiveVar(sidebarOpenVar);\n  const isLoggedIn = useReactiveVar(isLoggedInVar);\n\n  return (\n    <div className={`app ${theme}`}>\n      {isLoggedIn && sidebarOpen && <Sidebar />}\n      <main>\n        <Outlet />\n      </main>\n    </div>\n  );\n}\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/suspense-hooks.md (Tokens: ~5390 | Category: anthropic-skill) -->\n# Suspense Hooks Reference\n\n> **Note**: Suspense hooks are the recommended approach for data fetching in modern React applications (React 18+). They provide cleaner code, better loading state handling, and enable streaming SSR.\n\n## Table of Contents\n\n- [useSuspenseQuery Hook](#usesuspensequery-hook)\n- [useBackgroundQuery and useReadQuery](#usebackgroundquery-and-usereadquery)\n- [useLoadableQuery](#useloadablequery)\n- [createQueryPreloader](#createquerypreloader)\n- [useQueryRefHandlers](#usequeryrefhandlers)\n- [Distinguishing Queries with queryKey](#distinguishing-queries-with-querykey)\n- [Suspense Boundaries and Error Handling](#suspense-boundaries-and-error-handling)\n- [Transitions](#transitions)\n- [Avoiding Request Waterfalls](#avoiding-request-waterfalls)\n- [Fetch Policies](#fetch-policies)\n- [Streaming SSR or React Server Components](#streaming-ssr-or-react-server-components)\n- [Conditional Queries](#conditional-queries)\n\n## useSuspenseQuery Hook\n\nThe `useSuspenseQuery` hook is the Suspense-ready replacement for `useQuery`. It initiates a network request and causes the component calling it to suspend while the request is made. Unlike `useQuery`, it does not return `loading` states—these are handled by React's Suspense boundaries and error boundaries.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\nimport { GET_DOG } from \"./queries.generated\";\n\nfunction App() {\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Dog id=\"3\" />\n    </Suspense>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  // data is always defined when this component renders\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### Return Object\n\n```typescript\nconst {\n  data, // Query result data\n  dataState, // With default options: \"complete\" | \"streaming\"\n  // With returnPartialData: also \"partial\"\n  // With errorPolicy \"all\" or \"ignore\": also \"empty\"\n  error, // ApolloError (only when errorPolicy is \"all\" or \"ignore\")\n  networkStatus, // NetworkStatus.ready, NetworkStatus.loading, etc.\n  client, // Apollo Client instance\n  refetch, // Function to re-execute query\n  fetchMore, // Function for pagination\n} = useSuspenseQuery(QUERY, options);\n```\n\n### Key Differences from useQuery\n\n- **No `loading` boolean**: Component suspends instead of returning `loading: true`\n- **Error handling**: With default `errorPolicy` (`none`), errors are thrown and caught by error boundaries. With `errorPolicy: \"all\"` or `\"ignore\"`, the `error` property is returned and `data` may be `undefined`.\n- **`data` availability**: With default `errorPolicy` (`none`), `data` is guaranteed to be present when the component renders. With `errorPolicy: \"all\"` or `\"ignore\"`, when `dataState` is `empty`, `data` may be `undefined`.\n- **Suspense boundaries**: Must wrap component with `<Suspense>` to handle loading state\n\n### Changing Variables\n\nWhen variables change, `useSuspenseQuery` automatically re-runs the query. If the data is not in the cache, the component suspends again.\n\n```tsx\nimport { useState } from \"react\";\nimport { GET_DOGS } from \"./queries.generated\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => setSelectedDog(e.target.value)}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog.name}</div>\n      <div>Breed: {data.dog.breed}</div>\n    </>\n  );\n}\n```\n\n### Rendering Partial Data\n\nUse `returnPartialData` to render immediately with partial cache data instead of suspending. The component will still suspend if there is no data in the cache.\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    returnPartialData: true,\n  });\n\n  return (\n    <>\n      <div>Name: {data.dog?.name ?? \"Unknown\"}</div>\n      {data.dog?.breed && <div>Breed: {data.dog.breed}</div>}\n    </>\n  );\n}\n```\n\n## useBackgroundQuery and useReadQuery\n\nUse `useBackgroundQuery` with `useReadQuery` to avoid request waterfalls by starting a query in a parent component and reading the result in a child component. This pattern enables the parent to start fetching data before the child component renders.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useBackgroundQuery, useReadQuery } from \"@apollo/client/react\";\n\nfunction Parent() {\n  // Start fetching immediately\n  const [queryRef] = useBackgroundQuery(GET_DOG, {\n    variables: { id: \"3\" },\n  });\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Child queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction Child({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  // Read the query result\n  const { data } = useReadQuery(queryRef);\n\n  return <div>Name: {data.dog.name}</div>;\n}\n```\n\n### When to Use\n\n- **Avoiding waterfalls**: Start fetching data in a parent (preferably above a suspense boundary) before child components render\n- **Preloading data**: Begin fetching before the component that needs the data is ready\n- **Parallel queries**: Start multiple queries at once in a parent component\n\n### Return Values\n\n`useBackgroundQuery` returns a tuple:\n\n```typescript\nconst [\n  queryRef, // QueryRef to pass to useReadQuery\n  { refetch, fetchMore, subscribeToMore }, // Helper functions\n] = useBackgroundQuery(QUERY, options);\n```\n\n`useReadQuery` returns the query result:\n\n```typescript\nconst {\n  data, // Query result data (always defined)\n  dataState, // \"complete\" | \"streaming\" | \"partial\" | \"empty\"\n  error, // ApolloError (if errorPolicy allows)\n  networkStatus, // Detailed network state (1-8)\n} = useReadQuery(queryRef);\n```\n\n## useLoadableQuery\n\nUse `useLoadableQuery` to imperatively load a query in response to a user interaction (like a button click) instead of on component mount.\n\n### Basic Usage\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useLoadableQuery, useReadQuery } from \"@apollo/client/react\";\nimport { GET_GREETING } from \"./queries.generated\";\n\nfunction App() {\n  const [loadGreeting, queryRef] = useLoadableQuery(GET_GREETING);\n\n  return (\n    <>\n      <button\n        onClick={() => loadGreeting({ variables: { language: \"english\" } })}\n      >\n        Load Greeting\n      </button>\n      <Suspense fallback={<div>Loading...</div>}>\n        {queryRef && <Greeting queryRef={queryRef} />}\n      </Suspense>\n    </>\n  );\n}\n\nfunction Greeting({ queryRef }: { queryRef: QueryRef<GreetingData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>{data.greeting.message}</div>;\n}\n```\n\n### Return Values\n\n```typescript\nconst [\n  loadQuery, // Function to load the query\n  queryRef, // QueryRef (null until loadQuery is called)\n  { refetch, fetchMore, subscribeToMore, reset }, // Helper functions\n] = useLoadableQuery(QUERY, options);\n```\n\n### When to Use\n\n- **User-triggered fetching**: Load data in response to user actions\n- **Lazy loading**: Defer data fetching until it's actually needed\n- **Progressive disclosure**: Load data for UI elements that may not be initially visible\n\n## createQueryPreloader\n\nThe `createQueryPreloader` function creates a `preloadQuery` function that can be used to initiate queries outside of React components. This is useful for preloading data before a component renders, such as in route loaders or event handlers.\n\n### Basic Usage\n\n```tsx\nimport { ApolloClient, InMemoryCache } from \"@apollo/client\";\nimport { createQueryPreloader } from \"@apollo/client/react\";\n\nconst client = new ApolloClient({\n  uri: \"https://your-graphql-endpoint.com/graphql\",\n  cache: new InMemoryCache(),\n});\n\n// Create a preload function\nexport const preloadQuery = createQueryPreloader(client);\n```\n\n### Using preloadQuery with Route Loaders\n\n> **Note**: This example applies to React Router in non-framework mode. For React Router framework mode, see [integration-react-router.md](./integration-react-router.md).\n\nUse the preload function with React Router's `loader` function to begin loading data during route transitions:\n\n```tsx\nimport { preloadQuery } from \"@/lib/apollo-client\";\nimport { GET_DOG } from \"./queries.generated\";\n\n// React Router loader function\nexport async function loader({ params }: { params: { id: string } }) {\n  return preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n}\n\n// Route component\nexport default function DogRoute() {\n  const queryRef = useLoaderData();\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <DogDetails queryRef={queryRef} />\n    </Suspense>\n  );\n}\n\nfunction DogDetails({ queryRef }: { queryRef: QueryRef<DogData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <div>\n      <h1>{data.dog.name}</h1>\n      <p>Breed: {data.dog.breed}</p>\n    </div>\n  );\n}\n```\n\n### Preventing Route Transitions Until Query Loads\n\nUse `preloadQuery.toPromise(queryRef)` to prevent route transitions until the query finishes loading:\n\n```tsx\nexport async function loader({ params }: { params: { id: string } }) {\n  const queryRef = preloadQuery({\n    query: GET_DOG,\n    variables: { id: params.id },\n  });\n\n  // Wait for the query to complete before transitioning\n  return preloadQuery.toPromise(queryRef);\n}\n```\n\nWhen `preloadQuery.toPromise()` is used, the route transition waits for the query to complete, and the data renders immediately without showing a loading fallback.\n\n> **Note**: `preloadQuery.toPromise()` resolves with the `queryRef` itself (not the data) to encourage using `useReadQuery` for cache updates. If you need raw query data in your loader, use `client.query()` directly.\n\n### With Next.js Server Components\n\n> **Note**: For Next.js App Router, use the `PreloadQuery` component from `@apollo/client-integration-nextjs` instead. See [integration-nextjs.md](./integration-nextjs.md) for details.\n\n## useQueryRefHandlers\n\nThe `useQueryRefHandlers` hook provides access to `refetch` and `fetchMore` functions for queries initiated with `preloadQuery`, `useBackgroundQuery`, or `useLoadableQuery`. This is useful when you need to refetch or paginate data in components where the `queryRef` is passed through.\n\n> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.\n\n### Basic Usage\n\n```tsx\nimport { useQueryRefHandlers } from \"@apollo/client/react\";\n\nfunction Breeds({ queryRef }: { queryRef: QueryRef<BreedsData> }) {\n  const { refetch } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            refetch();\n          });\n        }}\n      >\n        {isPending ? \"Refetching...\" : \"Refetch breeds\"}\n      </button>\n      <ul>\n        {data.breeds.map((breed) => (\n          <li key={breed.id}>{breed.name}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n### With Pagination\n\nUse `fetchMore` to implement pagination:\n\n```tsx\nfunction Posts({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { fetchMore } = useQueryRefHandlers(queryRef);\n  const { data } = useReadQuery(queryRef);\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <div>\n      <ul>\n        {data.posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n      <button\n        disabled={isPending}\n        onClick={() => {\n          startTransition(() => {\n            fetchMore({\n              variables: {\n                offset: data.posts.length,\n              },\n            });\n          });\n        }}\n      >\n        {isPending ? \"Loading...\" : \"Load more\"}\n      </button>\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Preloaded queries**: Access refetch/fetchMore for queries initiated with `preloadQuery`\n- **Background queries**: Use in child components receiving `queryRef` from `useBackgroundQuery`\n- **Loadable queries**: Refetch or paginate queries initiated with `useLoadableQuery`\n- **React transitions**: Integrate with transitions to avoid showing loading fallbacks during refetches\n\n## Distinguishing Queries with queryKey\n\nApollo Client uses the combination of `query` and `variables` to uniquely identify each query. When multiple components use the same `query` and `variables`, they share the same identity and suspend at the same time, regardless of which component initiates the request.\n\nUse the `queryKey` option to ensure each hook has a unique identity:\n\n```tsx\nfunction UserProfile() {\n  // First query with unique key\n  const { data: userData } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-profile\"],\n  });\n\n  // Second query with same query and variables but different key\n  const { data: userPreview } = useSuspenseQuery(GET_USER, {\n    variables: { id: \"1\" },\n    queryKey: [\"user-preview\"],\n  });\n\n  return (\n    <div>\n      <UserCard user={userData.user} />\n      <UserSidebar user={userPreview.user} />\n    </div>\n  );\n}\n```\n\n### When to Use\n\n- **Multiple instances**: When rendering multiple components that use the same query and variables\n- **Preventing shared suspension**: When you want independent control over when each query suspends\n- **Separate cache entries**: When you need to maintain separate cache states for the same query\n\n> **Note**: Each item in the `queryKey` array must be a stable identifier to prevent infinite fetches.\n\n## Suspense Boundaries and Error Handling\n\n### Suspense Boundaries\n\nWrap components that use Suspense hooks with `<Suspense>` boundaries to handle loading states. Place boundaries strategically to control the granularity of loading indicators.\n\n```tsx\nfunction App() {\n  return (\n    <>\n      {/* Top-level loading for entire page */}\n      <Suspense fallback={<PageSpinner />}>\n        <Header />\n        <Content />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Content() {\n  return (\n    <>\n      <MainSection />\n      {/* Granular loading for sidebar */}\n      <Suspense fallback={<SidebarSkeleton />}>\n        <Sidebar />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Error Boundaries\n\nSuspense hooks throw errors to React error boundaries instead of returning them. Use error boundaries to handle GraphQL errors.\n\n```tsx\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nfunction App() {\n  return (\n    <ErrorBoundary\n      fallback={({ error }) => (\n        <div>\n          <h2>Something went wrong</h2>\n          <p>{error.message}</p>\n        </div>\n      )}\n    >\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id=\"3\" />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n```\n\n### Custom Error Policies\n\nUse `errorPolicy` to control how errors are handled:\n\n```tsx\nfunction Dog({ id }: { id: string }) {\n  const { data, error } = useSuspenseQuery(GET_DOG, {\n    variables: { id },\n    errorPolicy: \"all\", // Return both data and errors\n  });\n\n  return (\n    <>\n      <div>Name: {data?.dog?.name ?? \"Unknown\"}</div>\n      {error && <div>Warning: {error.message}</div>}\n    </>\n  );\n}\n```\n\n## Transitions\n\nUse React transitions to avoid showing loading UI when updating state. Transitions keep the previous UI visible while new data is fetching.\n\n### Using startTransition\n\n```tsx\nimport { useState, Suspense, startTransition } from \"react\";\n\nfunction DogSelector() {\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        value={selectedDog}\n        onChange={(e) => {\n          // Wrap state update in startTransition\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n### Using useTransition\n\nUse `useTransition` to get an `isPending` flag for visual feedback during transitions.\n\n```tsx\nimport { useState, Suspense, useTransition } from \"react\";\n\nfunction DogSelector() {\n  const [isPending, startTransition] = useTransition();\n  const { data } = useSuspenseQuery(GET_DOGS);\n  const [selectedDog, setSelectedDog] = useState(data.dogs[0].id);\n\n  return (\n    <>\n      <select\n        style={{ opacity: isPending ? 0.5 : 1 }}\n        value={selectedDog}\n        onChange={(e) => {\n          startTransition(() => {\n            setSelectedDog(e.target.value);\n          });\n        }}\n      >\n        {data.dogs.map((dog) => (\n          <option key={dog.id} value={dog.id}>\n            {dog.name}\n          </option>\n        ))}\n      </select>\n      <Suspense fallback={<div>Loading...</div>}>\n        <Dog id={selectedDog} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n## Avoiding Request Waterfalls\n\nRequest waterfalls occur when a child component waits for the parent to finish rendering before it can start fetching its own data. Use `useBackgroundQuery` to start fetching child data earlier in the component tree.\n\n> **Note**: When one query depends on the result of another query (e.g., the child query needs an ID from the parent query), the waterfall is unavoidable. The best solution is to restructure your schema to fetch all needed data in a single nested query.\n\n### Example: Independent Queries\n\nWhen queries don't depend on each other, use `useBackgroundQuery` to start them in parallel:\n\n```tsx\nconst GET_USER = gql`\n  query GetUser($id: String!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nconst GET_POSTS = gql`\n  query GetPosts {\n    posts {\n      id\n      title\n    }\n  }\n`;\n\nfunction Parent() {\n  // Both queries start immediately - no waterfall\n  const [userRef] = useBackgroundQuery(GET_USER, {\n    variables: { id: \"1\" },\n  });\n\n  const [postsRef] = useBackgroundQuery(GET_POSTS);\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserProfile queryRef={userRef} />\n      <PostsList queryRef={postsRef} />\n    </Suspense>\n  );\n}\n\nfunction UserProfile({ queryRef }: { queryRef: QueryRef<UserData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return <div>User: {data.user.name}</div>;\n}\n\nfunction PostsList({ queryRef }: { queryRef: QueryRef<PostsData> }) {\n  const { data } = useReadQuery(queryRef);\n\n  return (\n    <ul>\n      {data.posts.map((post) => (\n        <li key={post.id}>{post.title}</li>\n      ))}\n    </ul>\n  );\n}\n```\n\n## Fetch Policies\n\nSuspense hooks support most of the same fetch policies as `useQuery`, controlling how the query interacts with the cache. Note that `cache-only` and `standby` are not supported by Suspense hooks.\n\n| Policy              | Description                                                |\n| ------------------- | ---------------------------------------------------------- |\n| `cache-first`       | Return cached data if available, otherwise fetch (default) |\n| `cache-and-network` | Return cached data immediately, then fetch and update      |\n| `network-only`      | Always fetch, update cache, ignore cached data             |\n| `no-cache`          | Always fetch, never read or write cache                    |\n\n### Usage Examples\n\n```tsx\n// Always fetch fresh data\nconst { data } = useSuspenseQuery(GET_NOTIFICATIONS, {\n  fetchPolicy: \"network-only\",\n});\n\n// Prefer cached data\nconst { data } = useSuspenseQuery(GET_CATEGORIES, {\n  fetchPolicy: \"cache-first\",\n});\n\n// Show cached data while fetching fresh data\nconst { data } = useSuspenseQuery(GET_POSTS, {\n  fetchPolicy: \"cache-and-network\",\n});\n```\n\n## Streaming SSR or React Server Components\n\nApollo Client integrates with modern React frameworks that support Streaming SSR and React Server Components. For detailed setup instructions specific to your framework, see:\n\n- **Next.js App Router**: [integration-nextjs.md](./integration-nextjs.md) - Includes React Server Components, PreloadQuery component, and streaming SSR\n- **React Router**: [integration-react-router.md](./integration-react-router.md) - Framework mode with SSR support\n- **TanStack Start**: [integration-tanstack-start.md](./integration-tanstack-start.md) - Full-stack React framework with SSR\n\nThese guides cover:\n\n- Framework-specific client setup and configuration\n- Preloading queries for optimal performance\n- Streaming SSR with `useBackgroundQuery` and Suspense\n- Error handling in server-rendered environments\n\n## Conditional Queries\n\n### Using skipToken\n\nUse `skipToken` to conditionally skip queries without TypeScript issues. When `skipToken` is used, the component won't suspend and `data` will be `undefined`.\n\n```tsx\nimport { skipToken } from \"@apollo/client\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      email\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string | null }) {\n  const { data, dataState } = useSuspenseQuery(\n    GET_USER,\n    !userId ? skipToken : (\n      {\n        variables: { id: userId },\n      }\n    )\n  );\n\n  if (dataState !== \"complete\") {\n    return <p>Select a user</p>;\n  }\n\n  return <Profile user={data.user} />;\n}\n```\n\n### Conditional Rendering\n\nAlternatively, use conditional rendering to control when Suspense hooks are called. This provides better type safety and clearer component logic.\n\n```tsx\nfunction UserProfile({ userId }: { userId: string | null }) {\n  if (!userId) {\n    return <p>Select a user</p>;\n  }\n\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <UserDetails userId={userId} />\n    </Suspense>\n  );\n}\n\nfunction UserDetails({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <Profile user={data.user} />;\n}\n```\n\n> **Note**: Using conditional rendering with `skipToken` provides better type safety and avoids issues with required variables. The `skip` option is deprecated in favor of `skipToken`.\n\n<!-- File: docs/agent-skills/apollo-client/references/troubleshooting.md (Tokens: ~2536 | Category: anthropic-skill) -->\n# Troubleshooting Reference\n\n## Table of Contents\n\n- [Setup Issues](#setup-issues)\n- [Cache Issues](#cache-issues)\n- [TypeScript Issues](#typescript-issues)\n- [Performance Issues](#performance-issues)\n- [DevTools Usage](#devtools-usage)\n- [Common Error Messages](#common-error-messages)\n\n## Setup Issues\n\n### Provider Not Found\n\n**Error:** `Could not find \"client\" in the context or passed in as an option`\n\n**Cause:** Component is not wrapped with `ApolloProvider`.\n\n**Solution:**\n\n```tsx\n// Ensure ApolloProvider wraps your app\nimport { ApolloProvider } from \"@apollo/client\";\n\nfunction App() {\n  return (\n    <ApolloProvider client={client}>\n      <YourApp />\n    </ApolloProvider>\n  );\n}\n```\n\n### Multiple Apollo Clients\n\n**Problem:** Unintended cache isolation or conflicting states.\n\n**Solution:** Use a single client instance or explicitly manage multiple clients:\n\n```tsx\n// Single client (recommended)\nconst client = new ApolloClient({\n  /* ... */\n});\n\nexport function App() {\n  return (\n    <ApolloProvider client={client}>\n      <Router />\n    </ApolloProvider>\n  );\n}\n\n// Multiple clients (rare use case)\nconst publicClient = new ApolloClient({\n  uri: \"/public/graphql\",\n  cache: new InMemoryCache(),\n});\nconst adminClient = new ApolloClient({\n  uri: \"/admin/graphql\",\n  cache: new InMemoryCache(),\n});\n\nfunction AdminSection() {\n  return (\n    <ApolloProvider client={adminClient}>\n      <AdminDashboard />\n    </ApolloProvider>\n  );\n}\n```\n\n### Client Created in Component\n\n**Problem:** New client on every render causes cache loss.\n\n**Solution:** Create client outside component or use a ref pattern:\n\n```tsx\n// Bad - new client on every render\nfunction App() {\n  const client = new ApolloClient({\n    /* ... */\n  }); // Don't do this!\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Module-level client definition\n// Okay if there is a 100% guarantee this application will never use SSR\nconst client = new ApolloClient({\n  /* ... */\n});\nfunction App() {\n  return <ApolloProvider client={client}>...</ApolloProvider>;\n}\n\n// Good - store Apollo Client in a ref that is initialized once\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    storeRef.current = makeApolloClient();\n  }\n  return storeRef.current;\n}\n\n// Better - singleton global in non-SSR environments to survive unmounts\nconst singleton = Symbol.for(\"ApolloClientSingleton\");\ndeclare global {\n  interface Window {\n    [singleton]?: ApolloClient;\n  }\n}\n\nfunction useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {\n  const storeRef = useRef<ApolloClient | null>(null);\n  if (!storeRef.current) {\n    if (typeof window === \"undefined\") {\n      storeRef.current = makeApolloClient();\n    } else {\n      window[singleton] ??= makeApolloClient();\n      storeRef.current = window[singleton];\n    }\n  }\n  return storeRef.current;\n}\n// Note: this second option might need manual removal between tests\n```\n\n## Cache Issues\n\n### Stale Data Not Updating\n\n**Problem:** UI doesn't reflect mutations or other updates.\n\n**Solution 1:** Verify cache key identification:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    // Ensure proper identification\n    Product: {\n      keyFields: [\"id\"], // or ['sku'] if no id field\n    },\n  },\n});\n```\n\n**Solution 2:** Update cache after mutations:\n\n```tsx\nconst [deleteProduct] = useMutation(DELETE_PRODUCT, {\n  update: (cache, { data }) => {\n    cache.evict({ id: cache.identify(data.deleteProduct) });\n    cache.gc();\n  },\n});\n```\n\n**Solution 3:** Use appropriate fetch policy:\n\n```tsx\nconst { data } = useQuery(GET_PRODUCTS, {\n  fetchPolicy: \"cache-and-network\", // Always fetch fresh data\n});\n```\n\n### Missing Cache Updates After Mutation\n\n**Problem:** New items don't appear in lists after creation.\n\n**Solution:** Manually update the cache:\n\n```tsx\nconst [createProduct] = useMutation(CREATE_PRODUCT, {\n  update: (cache, { data }) => {\n    const existing = cache.readQuery<{ products: Product[] }>({\n      query: GET_PRODUCTS,\n    });\n\n    cache.writeQuery({\n      query: GET_PRODUCTS,\n      data: {\n        products: [...(existing?.products ?? []), data.createProduct],\n      },\n    });\n  },\n});\n```\n\n### Pagination Cache Issues\n\n**Problem:** Paginated data not merging correctly.\n\n**Solution:** Configure proper type policies:\n\n```typescript\nconst cache = new InMemoryCache({\n  typePolicies: {\n    Query: {\n      fields: {\n        products: {\n          keyArgs: [\"category\"], // Only category creates new cache entries\n          merge(existing = [], incoming) {\n            return [...existing, ...incoming];\n          },\n        },\n      },\n    },\n  },\n});\n```\n\n### Cache Normalization Problems\n\n**Problem:** Objects with same ID showing different data in different queries.\n\n**Debug:** Check cache contents:\n\n```typescript\n// In DevTools console or component\nconsole.log(client.cache.extract());\n```\n\n**Solution:** Ensure consistent `__typename` and `id` fields:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Always include id\n    name\n  }\n}\n```\n\n## TypeScript Issues\n\n### Type Generation Setup\n\n**Problem:** No type safety for GraphQL operations.\n\n**Solution:** Set up GraphQL Code Generator with the [recommended starter configuration](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration), as described in the [Skill](../SKILL.md).\n\n### Using Generated Types\n\n```tsx\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUsersDocument, GetUsersQuery } from \"./generated/graphql\";\n\nfunction UserList() {\n  // Fully typed without manual type annotations\n  const { data, loading, error } = useQuery(GetUsersDocument);\n\n  // data.users is automatically typed as GetUsersQuery['users']\n  return (\n    <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>\n  );\n}\n```\n\n## Performance Issues\n\n### Over-Fetching\n\n**Problem:** Fetching more data than needed.\n\n**Solution:** Select only required fields:\n\n```graphql\n# Bad - fetching everything\nquery GetUsers {\n  users {\n    id\n    name\n    email\n    profile { ... }\n    posts { ... }\n    friends { ... }\n  }\n}\n\n# Good - fetch what's needed\nquery GetUserNames {\n  users {\n    id\n    name\n  }\n}\n```\n\n### N+1 Queries\n\n**Problem:** Multiple network requests for related data.\n\n**Solution:** Structure queries to batch requests. Best practice: use query colocation and compose queries from fragments defined on child components.\n\n```graphql\n# Bad - separate queries\nquery GetUser($id: ID!) {\n  user(id: $id) {\n    id\n    name\n  }\n}\nquery GetUserPosts($userId: ID!) {\n  posts(userId: $userId) {\n    id\n    title\n  }\n}\n\n# Good - single query\nquery GetUserWithPosts($id: ID!) {\n  user(id: $id) {\n    id\n    name\n    posts {\n      id\n      title\n    }\n  }\n}\n```\n\n### Unnecessary Re-Renders\n\n**Problem:** Components re-render when unrelated cache data changes.\n\n**Solution:** Use `useFragment` and data masking for selective field reading. If that is not possible, `useQuery` with `@nonreactive` directives might be an alternative.\n\n```tsx\n// Prefer useFragment with data masking\nconst { data } = useFragment({\n  fragment: USER_FRAGMENT,\n  from: { __typename: \"User\", id },\n});\n\n// Alternative: use @nonreactive directive\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n      # This field won't trigger re-renders when it changes\n      metadata @nonreactive {\n        lastSeen\n        preferences\n      }\n    }\n  }\n`;\n\nconst { data } = useQuery(GET_USER, {\n  variables: { id },\n});\n```\n\n### Cache Misses\n\n**Debug:** Use Apollo DevTools to inspect cache.\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  // DevTools are enabled by default in development\n  // Only configure this when you need to enable them in production\n  devtools: {\n    enabled: true,\n  },\n});\n```\n\n## DevTools Usage\n\n### Installing Apollo DevTools\n\nInstall the browser extension:\n\n- [Chrome](https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)\n- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/)\n\n### Enabling DevTools\n\nDevTools are enabled by default in development. Only configure this setting if you need to enable them in production:\n\n```typescript\nconst client = new ApolloClient({\n  cache: new InMemoryCache(),\n  devtools: {\n    enabled: true, // Set to true to enable in production\n  },\n});\n```\n\n### DevTools Features\n\n1. **Cache Inspector**: View normalized cache contents\n2. **Queries**: See active queries and their states\n3. **Mutations**: Track mutation history\n4. **Explorer**: Build and test queries against your schema\n5. **Memoization Limits**: Monitor and track cache memoization\n6. **Cache Writes**: Track all writes to the cache\n\n### Debugging Cache\n\n```typescript\n// Log cache contents\nconsole.log(JSON.stringify(client.cache.extract(), null, 2));\n\n// Check specific object using cache.identify\nconsole.log(\n  client.cache.readFragment({\n    id: cache.identify({ __typename: \"User\", id: 1 }),\n    fragment: gql`\n      fragment _ on User {\n        id\n        name\n        email\n      }\n    `,\n  })\n);\n```\n\n## Common Error Messages\n\n### \"Missing field 'X' in {...}\"\n\n**Cause:** Query doesn't include required field for cache normalization.\n\n**Solution:** Include `id` and `__typename`:\n\n```graphql\nquery GetUsers {\n  users {\n    id # Required for caching\n    __typename # Usually added automatically\n    name\n  }\n}\n```\n\n**Additional advice**: Read the full error message thoroughly.\n\n### \"Store reset while query was in flight\"\n\n**Cause:** `client.resetStore()` called during active queries.\n\n**Solution:** Wait for queries to complete or use `clearStore()`:\n\n```typescript\n// Option 1: Clear without refetching\nawait client.clearStore();\n\n// Option 2: Reset and refetch active queries\nawait client.resetStore();\n```\n\n### \"Invariant Violation: X\"\n\n**Cause:** Various configuration or usage errors.\n\n**Common fixes:**\n\n- Ensure `ApolloProvider` wraps the component tree\n- Check that `gql` tagged templates are valid GraphQL\n- Verify cache configuration matches your schema\n\n### \"Cannot read property 'X' of undefined\"\n\n**Cause:** Accessing data before query completes.\n\n**Solution:** Check `dataState` for proper type narrowing:\n\n```tsx\nconst { data, dataState } = useQuery(GET_USER);\n\n// dataState can be \"complete\", \"partial\", \"streaming\", or \"empty\"\n// It describes the completeness of the data, not a loading state\nif (dataState === \"empty\") return <Spinner />;\n\n// Now data is guaranteed to exist\nreturn <div>{data.user.name}</div>;\n\n// Or use optional chaining\nreturn <div>{data?.user?.name}</div>;\n```\n\n<!-- File: docs/agent-skills/apollo-client/references/typescript-codegen.md (Tokens: ~898 | Category: anthropic-skill) -->\n# TypeScript Code Generation\n\nThis guide covers setting up GraphQL Code Generator for type-safe Apollo Client usage with TypeScript.\n\n## Installation\n\n```bash\nnpm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node\n```\n\n## Configuration\n\nCreate a `codegen.ts` file in your project root:\n\n```typescript\n// codegen.ts\nimport { CodegenConfig } from \"@graphql-codegen/cli\";\n\nconst config: CodegenConfig = {\n  overwrite: true,\n  schema: \"<URL_OF_YOUR_GRAPHQL_API>\",\n  // This assumes that all your source files are in a top-level `src/` directory - you might need to adjust this to your file structure\n  documents: [\"src/**/*.{ts,tsx}\"],\n  // Don't exit with non-zero status when there are no documents\n  ignoreNoDocuments: true,\n  generates: {\n    // Use a path that works the best for the structure of your application\n    \"./src/types/__generated__/graphql.ts\": {\n      plugins: [\"typescript\", \"typescript-operations\", \"typed-document-node\"],\n      config: {\n        avoidOptionals: {\n          // Use `null` for nullable fields instead of optionals\n          field: true,\n          // Allow nullable input fields to remain unspecified\n          inputValue: false,\n        },\n        // Use `unknown` instead of `any` for unconfigured scalars\n        defaultScalarType: \"unknown\",\n        // Apollo Client always includes `__typename` fields\n        nonOptionalTypename: true,\n        // Apollo Client doesn't add the `__typename` field to root types so\n        // don't generate a type for the `__typename` for root operation types.\n        skipTypeNameForRoot: true,\n      },\n    },\n  },\n};\n\nexport default config;\n```\n\n## Enable Data Masking\n\nTo enable data masking with GraphQL Code Generator, create a type declaration file to inform Apollo Client about the generated types:\n\n```typescript\n// apollo-client.d.ts\nimport { GraphQLCodegenDataMasking } from \"@apollo/client/masking\";\n\ndeclare module \"@apollo/client\" {\n  export interface TypeOverrides\n    extends GraphQLCodegenDataMasking.TypeOverrides {}\n}\n```\n\n## Running Code Generation\n\nAdd a script to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"codegen\": \"graphql-codegen\"\n  }\n}\n```\n\nRun code generation:\n\n```bash\nnpm run codegen\n```\n\n## Usage with Apollo Client\n\nThe typed-document-node plugin generates `TypedDocumentNode` types that Apollo Client hooks automatically infer.\n\n### Defining Operations\n\nDefine your operations inline with the `if (false)` pattern. This allows GraphQL Code Generator to detect and extract operations without executing the code at runtime (bundlers omit this dead code during minification):\n\n```typescript\nimport { gql } from \"@apollo/client\";\n\n// This query will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.\nif (false) {\n  gql`\n    query GetUser($id: ID!) {\n      user(id: $id) {\n        id\n        name\n        email\n      }\n    }\n  `;\n}\n```\n\n### Using Generated Types\n\nAfter running `npm run codegen`, import the generated `TypedDocumentNode`:\n\n```typescript\nimport { useQuery } from \"@apollo/client/react\";\nimport { GetUserDocument } from \"./queries.generated\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  // Types are automatically inferred from GetUserDocument\n  const { data } = useQuery(GetUserDocument, {\n    variables: { id: userId },\n  });\n\n  // ... other logic ...\n\n  return <div>{data?.user.name}</div>;\n}\n```\n\n## Important Notes\n\n- The typed-document-node plugin might have a bundle size tradeoff but can prevent inconsistencies and is best suited for usage with LLMs, so it is recommended for most applications.\n- See the [GraphQL Code Generator documentation](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration) for other recommended configuration patterns if required.\n- Apollo Client hooks automatically infer types from `TypedDocumentNode` - never use manual generics like `useQuery<QueryType, VariablesType>()`.\n\n<!-- File: docs/agent-skills/apollo-client/SKILL.md (Tokens: ~1670 | Category: anthropic-skill) -->\n---\nname: apollo-client\ndescription: >\n  Guide for building React applications with Apollo Client 4.x. Use this skill when:\n  (1) setting up Apollo Client in a React project,\n  (2) writing GraphQL queries or mutations with hooks,\n  (3) configuring caching or cache policies,\n  (4) managing local state with reactive variables,\n  (5) troubleshooting Apollo Client errors or performance issues.\nlicense: MIT\ncompatibility: React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.\nmetadata:\n  author: apollographql\n  version: \"1.0.0\"\nallowed-tools: Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep\n---\n\n# Apollo Client 4.x Guide\n\nApollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Version 4.x brings improved caching, better TypeScript support, and React 19 compatibility.\n\n## Integration Guides\n\nChoose the integration guide that matches your application setup:\n\n- **[Client-Side Apps](references/integration-client.md)** - For client-side React applications without SSR (Vite, Create React App, etc.)\n- **[Next.js App Router](references/integration-nextjs.md)** - For Next.js applications using the App Router with React Server Components\n- **[React Router Framework Mode](references/integration-react-router.md)** - For React Router 7 applications with streaming SSR\n- **[TanStack Start](references/integration-tanstack-start.md)** - For TanStack Start applications with modern routing\n\nEach guide includes installation steps, configuration, and framework-specific patterns optimized for that environment.\n\n## Quick Reference\n\n### Basic Query\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\n\nconst GET_USER = gql`\n  query GetUser($id: ID!) {\n    user(id: $id) {\n      id\n      name\n    }\n  }\n`;\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { loading, error, data, dataState } = useQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  if (loading) return <p>Loading...</p>;\n  if (error) return <p>Error: {error.message}</p>;\n\n  // TypeScript note: for stricter type narrowing, you can also check `dataState === \"complete\"` before accessing data\n  return <div>{data?.user.name}</div>;\n}\n```\n\n### Basic Mutation\n\n```tsx\nimport { gql } from \"@apollo/client\";\nimport { useMutation } from \"@apollo/client/react\";\n\nconst CREATE_USER = gql`\n  mutation CreateUser($input: CreateUserInput!) {\n    createUser(input: $input) {\n      id\n      name\n    }\n  }\n`;\n\nfunction CreateUserForm() {\n  const [createUser, { loading, error }] = useMutation(CREATE_USER);\n\n  const handleSubmit = async (name: string) => {\n    await createUser({ variables: { input: { name } } });\n  };\n\n  return <button onClick={() => handleSubmit(\"John\")}>Create User</button>;\n}\n```\n\n### Suspense Query\n\n```tsx\nimport { Suspense } from \"react\";\nimport { useSuspenseQuery } from \"@apollo/client/react\";\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data } = useSuspenseQuery(GET_USER, {\n    variables: { id: userId },\n  });\n\n  return <div>{data.user.name}</div>;\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<p>Loading user...</p>}>\n      <UserProfile userId=\"1\" />\n    </Suspense>\n  );\n}\n```\n\n## Reference Files\n\nDetailed documentation for specific topics:\n\n- [TypeScript Code Generation](references/typescript-codegen.md) - GraphQL Code Generator setup for type-safe operations\n- [Queries](references/queries.md) - useQuery, useLazyQuery, polling, refetching\n- [Suspense Hooks](references/suspense-hooks.md) - useSuspenseQuery, useBackgroundQuery, useReadQuery, useLoadableQuery\n- [Mutations](references/mutations.md) - useMutation, optimistic UI, cache updates\n- [Fragments](references/fragments.md) - Fragment colocation, useFragment, useSuspenseFragment, data masking\n- [Caching](references/caching.md) - InMemoryCache, typePolicies, cache manipulation\n- [State Management](references/state-management.md) - Reactive variables, local state\n- [Error Handling](references/error-handling.md) - Error policies, error links, retries\n- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions\n\n## Key Rules\n\n### Query Best Practices\n\n- **Each page should generally only have one query, composed from colocated fragments.** Use `useFragment` or `useSuspenseFragment` in all non-page-components. Use `@defer` to allow slow fields below the fold to stream in later and avoid blocking the page load.\n- **Fragments are for colocation, not reuse.** Each fragment should describe exactly the data needs of a specific component, not be shared across components for common fields. See [Fragments reference](references/fragments.md) for details on fragment colocation and data masking.\n- Always handle `loading` and `error` states in UI when using non-suspenseful hooks (`useQuery`, `useLazyQuery`). When using Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`), React handles this through `<Suspense>` boundaries and error boundaries.\n- Use `fetchPolicy` to control cache behavior per query\n- Use the TypeScript type server to look up documentation for functions and options (Apollo Client has extensive docblocks)\n\n### Mutation Best Practices\n\n- **If the schema permits, mutation return values should return everything necessary to update the cache.** Neither manual updates nor refetching should be necessary.\n- If the mutation response is insufficient, carefully weigh manual cache manipulation vs refetching. Manual updates risk missing server logic. Consider optimistic updates with a granular refetch if needed.\n- Handle errors gracefully in the UI\n- Use `refetchQueries` sparingly (prefer letting the cache update automatically)\n\n### Caching Best Practices\n\n- Configure `keyFields` for types without `id` field\n- Disable normalization by setting `keyFields: false` for types that don't include an identifier and are meant to group related fields under the parent\n- Use `typePolicies` for pagination and computed fields\n- Understand cache normalization to debug issues\n- **Enable [data masking](references/fragments.md#data-masking) for all new applications** - it prevents components from accessing fragment data they don't own, enforcing proper data boundaries and preventing over-rendering\n\n### Performance\n\n- Avoid over-fetching with proper field selection\n- Configure appropriate `fetchPolicy` per use case\n- Use `@defer` for incremental delivery of deferred query parts, and `@stream` for streaming list fields (`@stream` available in Apollo Client 4.1+)\n- **Prefer Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications** for better loading state handling and code simplicity\n\n## Ground Rules\n\n- ALWAYS use Apollo Client 4.x patterns (not v3 or earlier)\n- ALWAYS wrap your app with `ApolloProvider`\n- ALWAYS handle loading and error states when using non-suspenseful hooks\n- PREFER Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications for better DX\n- NEVER store Apollo Client in React state (use module-level or context)\n- PREFER `cache-first` for read-heavy data, `network-only` for real-time data\n- USE TypeScript for better type safety with GraphQL\n- IMPLEMENT proper cache updates instead of refetching entire queries\n- ADVISE the human steering you to look into Apollo DevTools when collaboratively debugging Apollo Client issues\n\n<!-- File: .claude/commands/chores.md (Tokens: ~171 | Category: command-prompt) -->\n---\nallowed-tools: Bash(git commit:*)\ndescription: Chores\n---\n\n## Context\n\n- Current git status: !`git status`\n- Current git diff (staged and unstaged changes): !`git diff HEAD`\n- Current branch: !`git branch --show-current`\n- Recent commits: !`git log --oneline -10`\n\n## Your task\n\nFetch the latest changes from the remote repository. If the current branch is not up to date, don't do anything, but inform the user about it.\n\nTake a note if the current git status is clean.\n\nRun the npm scripts `format`, `build`, `extract-api:only`, `update-size-limits` after each other in that order.\n\nIf before running the scripts, the git status was clean, create a new commit with the message \"chores\".\n\n<!-- File: .cursorrules (react-graphql-apollo-client-cursorrules-prompt-file.mdc) (Tokens: ~237 | Category: cursor-rule) -->\n---\ndescription: \"Cursor rules for GraphQL development with Apollo Client integration.\"\nglobs: **/*\nalwaysApply: false\n---\n// React + GraphQL (Apollo Client) .cursorrules\n\n// Prefer functional components with hooks\n\nconst preferFunctionalComponents = true;\n\n// GraphQL and Apollo Client best practices\n\nconst graphqlBestPractices = [\n  \"Use Apollo Client for state management and data fetching\",\n  \"Implement query components for data fetching\",\n  \"Utilize mutations for data modifications\",\n  \"Use fragments for reusable query parts\",\n  \"Implement proper error handling and loading states\",\n];\n\n// Folder structure\n\nconst folderStructure = `\nsrc/\n  components/\n  graphql/\n    queries/\n    mutations/\n    fragments/\n  hooks/\n  pages/\n  utils/\n`;\n\n// Additional instructions\n\nconst additionalInstructions = `\n1. Use Apollo Provider at the root of your app\n2. Implement custom hooks for Apollo operations\n3. Use TypeScript for type safety with GraphQL operations\n4. Utilize Apollo Client's caching capabilities\n5. Implement proper error boundaries for GraphQL errors\n6. Use Apollo Client DevTools for debugging\n7. Follow naming conventions for queries, mutations, and fragments\n`;\n</yakaai_skills>"}