graphql-apollo-client

pluginagentmarketplace's avatarfrom pluginagentmarketplace

Build React apps with Apollo Client - queries, mutations, cache, and subscriptions

⭐1stars🔀0forks📁View on GitHub🕐Updated Dec 31, 2025

When & Why to Use This Skill

This Claude skill enables the seamless integration of Apollo Client into React applications for efficient GraphQL data management. It provides comprehensive patterns for executing queries and mutations, managing sophisticated local caches, and implementing real-time subscriptions. By leveraging this skill, developers can accelerate the creation of data-driven UIs with built-in support for optimistic updates, pagination, and robust error handling, ensuring high-performance and scalable front-end architecture.

Use Cases

  • Developing data-intensive React dashboards with efficient fetching, caching, and automated pagination logic using useQuery.
  • Implementing high-performance user interfaces with optimistic updates to provide an instantaneous user experience during data mutations.
  • Integrating real-time features such as live chat, notifications, or collaborative editing using GraphQL subscriptions and WebSocket links.
  • Configuring standardized API clients with advanced middleware for authentication headers, logging, and centralized error management.
namegraphql-apollo-client
descriptionBuild React apps with Apollo Client - queries, mutations, cache, and subscriptions
sasmp_version"1.3.0"
bonded_agent05-graphql-apollo-client
bond_typePRIMARY_BOND
version"2.0.0"
complexityintermediate
estimated_time"4-6 hours"
prerequisites["graphql-fundamentals"]

Apollo Client Skill

Master GraphQL in React applications

Overview

Learn to integrate Apollo Client with React, including hooks, cache management, optimistic updates, and real-time subscriptions.


Quick Reference

Hook Purpose Returns
useQuery Fetch data { data, loading, error, refetch }
useMutation Modify data [mutate, { data, loading, error }]
useSubscription Real-time { data, loading, error }
useLazyQuery On-demand fetch [execute, { data, loading }]

Core Patterns

1. Client Setup

import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  createHttpLink,
  from
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';

// HTTP connection
const httpLink = createHttpLink({
  uri: 'http://localhost:4000/graphql',
  credentials: 'include',
});

// Auth header
const authLink = setContext((_, { headers }) => ({
  headers: {
    ...headers,
    authorization: localStorage.getItem('token')
      ? `Bearer ${localStorage.getItem('token')}`
      : '',
  },
}));

// Error handling
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, extensions }) => {
      if (extensions?.code === 'UNAUTHENTICATED') {
        window.location.href = '/login';
      }
    });
  }
});

// Cache with type policies
const cache = new InMemoryCache({
  typePolicies: {
    User: { keyFields: ['id'] },
    Query: {
      fields: {
        users: {
          keyArgs: ['filter'],
          merge(existing, incoming, { args }) {
            if (!args?.after) return incoming;
            return {
              ...incoming,
              edges: [...(existing?.edges || []), ...incoming.edges],
            };
          },
        },
      },
    },
  },
});

// Client
const client = new ApolloClient({
  link: from([errorLink, authLink, httpLink]),
  cache,
});

// Provider
function App() {
  return (
    <ApolloProvider client={client}>
      <Router />
    </ApolloProvider>
  );
}

2. Queries

import { gql, useQuery } from '@apollo/client';

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

function UserProfile({ userId }) {
  const { data, loading, error, refetch } = useQuery(GET_USER, {
    variables: { id: userId },
    // Options
    fetchPolicy: 'cache-and-network',
    pollInterval: 60000, // Refetch every minute
    skip: !userId,       // Skip if no userId
  });

  if (loading) return <Spinner />;
  if (error) return <Error onRetry={refetch} />;

  return <div>{data.user.name}</div>;
}

// Pagination
function UserList() {
  const { data, fetchMore, networkStatus } = useQuery(GET_USERS, {
    variables: { first: 10 },
    notifyOnNetworkStatusChange: true,
  });

  const loadMore = () => {
    fetchMore({
      variables: { after: data.users.pageInfo.endCursor },
    });
  };

  return (
    <>
      {data?.users.edges.map(({ node }) => (
        <UserCard key={node.id} user={node} />
      ))}
      {data?.users.pageInfo.hasNextPage && (
        <button onClick={loadMore}>Load More</button>
      )}
    </>
  );
}

3. Mutations

const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      user { id name email }
      errors { field message }
    }
  }
`;

function CreateUserForm() {
  const [createUser, { loading }] = useMutation(CREATE_USER, {
    // Update cache after success
    update(cache, { data }) {
      if (!data?.createUser.user) return;

      cache.modify({
        fields: {
          users(existing = { edges: [] }) {
            const newRef = cache.writeFragment({
              data: data.createUser.user,
              fragment: gql`fragment NewUser on User { id name email }`,
            });
            return {
              ...existing,
              edges: [{ node: newRef }, ...existing.edges],
            };
          },
        },
      });
    },

    // Optimistic response
    optimisticResponse: (vars) => ({
      createUser: {
        __typename: 'CreateUserPayload',
        user: {
          __typename: 'User',
          id: 'temp-' + Date.now(),
          ...vars.input,
        },
        errors: [],
      },
    }),
  });

  const handleSubmit = async (input) => {
    const { data } = await createUser({ variables: { input } });
    if (data?.createUser.errors.length) {
      // Handle validation errors
    }
  };

  return <Form onSubmit={handleSubmit} loading={loading} />;
}

4. Optimistic Updates

// Delete
const [deleteUser] = useMutation(DELETE_USER, {
  optimisticResponse: {
    deleteUser: { success: true, id: userId },
  },
  update(cache, { data }) {
    cache.evict({ id: cache.identify({ __typename: 'User', id: userId }) });
    cache.gc();
  },
});

// Toggle
const [toggleLike] = useMutation(TOGGLE_LIKE, {
  optimisticResponse: {
    toggleLike: {
      __typename: 'Post',
      id: post.id,
      isLiked: !post.isLiked,
      likeCount: post.isLiked ? post.likeCount - 1 : post.likeCount + 1,
    },
  },
});

5. Subscriptions

import { useSubscription } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { split } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';

// WebSocket link
const wsLink = new GraphQLWsLink(
  createClient({
    url: 'ws://localhost:4000/graphql',
    connectionParams: () => ({
      authToken: localStorage.getItem('token'),
    }),
  })
);

// Split between HTTP and WS
const splitLink = split(
  ({ query }) => {
    const def = getMainDefinition(query);
    return def.kind === 'OperationDefinition' && def.operation === 'subscription';
  },
  wsLink,
  httpLink,
);

// Usage
const MESSAGE_SUB = gql`
  subscription OnMessage($channelId: ID!) {
    messageSent(channelId: $channelId) {
      id
      content
      sender { name }
    }
  }
`;

function Chat({ channelId }) {
  const { data } = useSubscription(MESSAGE_SUB, {
    variables: { channelId },
  });

  // New message in data?.messageSent
}

Troubleshooting

Issue Cause Solution
Stale data Cache not updated Add update function
Duplicates Missing keyFields Configure typePolicies
Refetch loop Variables object recreated useMemo variables
No subscription Missing split link Add wsLink

Debug

// Cache inspection
console.log(client.cache.extract());

// Apollo DevTools
// Install browser extension

// Logging link
import { ApolloLink } from '@apollo/client';

const logLink = new ApolloLink((operation, forward) => {
  console.log('Request:', operation.operationName);
  return forward(operation);
});

Usage

Skill("graphql-apollo-client")
  • graphql-fundamentals - Query syntax
  • graphql-apollo-server - Server integration
  • graphql-codegen - Type generation
  • 05-graphql-apollo-client - For detailed guidance