--- name: graphql-apollo-client description: Build React apps with Apollo Client - queries, mutations, cache, and subscriptions sasmp_version: "1.3.0" bonded_agent: 05-graphql-apollo-client bond_type: PRIMARY_BOND version: "2.0.0" complexity: intermediate 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 ```typescript 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 ( ); } ``` ### 2. Queries ```typescript 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 ; if (error) return ; return
{data.user.name}
; } // 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 }) => ( ))} {data?.users.pageInfo.hasNextPage && ( )} ); } ``` ### 3. Mutations ```typescript 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
; } ``` ### 4. Optimistic Updates ```typescript // 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 ```typescript 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 ```typescript // 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") ``` ## Related Skills - `graphql-fundamentals` - Query syntax - `graphql-apollo-server` - Server integration - `graphql-codegen` - Type generation ## Related Agent - `05-graphql-apollo-client` - For detailed guidance