From harness-claude
Guides structuring GraphQL client code with fragment co-location, normalized cache configuration, and optimistic updates for responsive UIs in React/Vue/Svelte apps.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:graphql-client-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Structure GraphQL client code with fragments, cache normalization, and optimistic updates for responsive UIs
Structure GraphQL client code with fragments, cache normalization, and optimistic updates for responsive UIs
.graphql file) as the component that renders the data. This makes data dependencies explicit.const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
...UserAvatar
}
}
${USER_AVATAR_FRAGMENT}
`;
function UserProfile({ userId }: { userId: string }) {
const { data, loading, error } = useQuery(GET_USER, { variables: { id: userId } });
if (loading) return <Skeleton />;
if (error) return <ErrorBanner error={error} />;
return <div>{data.user.name}</div>;
}
const USER_AVATAR_FRAGMENT = gql`
fragment UserAvatar on User {
id
avatarUrl
name
}
`;
// Reuse in any query that needs avatar data
typePolicies. Apollo Client's InMemoryCache normalizes by __typename:id. Customize merge behavior, pagination, and key fields in typePolicies.const cache = new InMemoryCache({
typePolicies: {
User: {
keyFields: ['id'],
},
Query: {
fields: {
feed: offsetLimitPagination(),
},
},
},
});
optimisticResponse that mirrors the expected server response. The cache updates immediately; the real response replaces it when it arrives.const [toggleLike] = useMutation(TOGGLE_LIKE, {
optimisticResponse: {
toggleLike: {
__typename: 'Post',
id: postId,
isLiked: !currentlyLiked,
likeCount: currentlyLiked ? count - 1 : count + 1,
},
},
});
id and __typename, the cache updates automatically.refetchQueries: Re-execute specific queries after the mutation. Simple but costs a network round-trip.update function: Manually read and write the cache for complex updates (e.g., adding an item to a list).const [addComment] = useMutation(ADD_COMMENT, {
update(cache, { data: { addComment } }) {
cache.modify({
id: cache.identify({ __typename: 'Post', id: postId }),
fields: {
comments(existing = []) {
const newRef = cache.writeFragment({
data: addComment,
fragment: COMMENT_FIELDS,
});
return [...existing, newRef];
},
},
});
},
});
Handle loading and error states consistently. Create reusable patterns — a <QueryResult> wrapper component or custom hooks that standardize loading/error/empty states across the app.
Use fetchPolicy intentionally.
cache-first (default): Read from cache, fetch only on miss. Best for stable data.network-only: Always fetch, update cache. Best for frequently changing data.cache-and-network: Return cached data immediately, then update from network. Best UX for lists.no-cache: Skip the cache entirely. Use for one-off sensitive data.Avoid over-fetching with field-level selections. Only request the fields the component needs. The normalized cache works best when queries request predictable field sets.
Apollo Client vs. urql vs. lightweight clients: Apollo Client offers the richest cache and ecosystem but is the largest bundle (~40KB). urql is smaller (~15KB) with a plugin-based architecture (exchanges). For simple use cases, graphql-request (~5KB) provides a fetch wrapper without caching.
Cache normalization explained: Apollo splits query results into individual objects keyed by __typename:id. When a mutation returns an updated User, every query that references that user sees the update. This works only if every queried type has a stable id field.
Fragment colocation pattern (Relay-style): Each component declares a fragment for the data it needs. Parent components spread those fragments into their queries. This creates a clear contract: the component works if and only if its fragment is included.
Polling vs. subscriptions: Use pollInterval for data that changes infrequently (dashboard stats). Use subscriptions for real-time data (chat messages, live scores). Polling is simpler to implement and does not require WebSocket infrastructure.
Common mistakes:
__typename in optimistic responses (cache cannot normalize without it)refetchQueries for every mutation instead of leveraging automatic cache updateserror state from useQuery (silent failures)https://www.apollographql.com/docs/react/
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeImplements Apollo Client patterns for GraphQL queries, mutations, cache management, and local state in React applications.
Guide for building React applications with Apollo Client 4.x. Covers setup, GraphQL queries/mutations with hooks, caching policies, reactive variables, and troubleshooting.
Covers GraphQL schema design, resolvers, DataLoader for N+1 prevention, federation, subscriptions, and client integration with Apollo/urql.