Install any skill in seconds. Free to start, no credit card required.
Get Started Free →You are an expert in Apollo Client, the comprehensive GraphQL client for React applications. You help developers fetch data with GraphQL queries and mutations, manage local and remote state with Apollo's normalized cache, implement optimistic UI updates, handle pagination, and configure authentication — providing a complete data management solution for GraphQL-powered apps.
.claude/skills/terminalskills-apollo-client/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-17 | ✗→✓ | ▲ Improved | 101% | 0% |
| case-08 | ✓→✗ | ▼ Worse | 325% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 51% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 83% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 39% | 0% |
You are an expert in Apollo Client, the comprehensive GraphQL client for React applications. You help developers fetch data with GraphQL queries and mutations, manage local and remote state with Apollo's normalized cache, implement optimistic UI updates, handle pagination, and configure authentication — providing a complete data management solution for GraphQL-powered apps.
tsximport { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery, useMutation } from "@apollo/client"; const client = new ApolloClient({ uri: "https://api.example.com/graphql", cache: new InMemoryCache({ typePolicies: { Query: { fields: { posts: { keyArgs: ["filter"], merge(existing = [], incoming) { return [...existing, ...incoming]; } }, }, }, }, }), headers: { Authorization: `Bearer ${getToken()}` }, }); const GET_POSTS = gql` query GetPosts($limit: Int!, $offset: Int!, $filter: PostFilter) { posts(limit: $limit, offset: $offset, filter: $filter) { id title excerpt author { id name avatar } createdAt } } `; function PostList({ filter }: { filter?: PostFilter }) { const { data, loading, error, fetchMore } = useQuery(GET_POSTS, { variables: { limit: 10, offset: 0, filter }, }); if (loading) return <Skeleton />; if (error) return <Error message={error.message} />; return ( <div> {data.posts.map(post => <PostCard key={post.id} post={post} />)} <button onClick={() => fetchMore({ variables: { offset: data.posts.length } })}> Load more </button> </div> ); }
tsxconst CREATE_POST = gql` mutation CreatePost($input: CreatePostInput!) { createPost(input: $input) { id title excerpt author { id name } createdAt } } `; function CreatePostForm() { const [createPost, { loading }] = useMutation(CREATE_POST, { optimisticResponse: (vars) => ({ createPost: { __typename: "Post", id: "temp-id", title: vars.input.title, excerpt: vars.input.body.slice(0, 200), author: { __typename: "User", id: currentUser.id, name: currentUser.name }, createdAt: new Date().toISOString(), }, }), update(cache, { data: { createPost } }) { cache.modify({ fields: { posts(existing = []) { const ref = cache.writeFragment({ data: createPost, fragment: POST_FRAGMENT }); return [ref, ...existing]; }, }, }); }, }); const handleSubmit = (data) => createPost({ variables: { input: data } }); return <Form onSubmit={handleSubmit} loading={loading} />; }
tsximport { ApolloClient, createHttpLink, ApolloLink } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; import { onError } from "@apollo/client/link/error"; const httpLink = createHttpLink({ uri: "/graphql" }); const authLink = setContext((_, { headers }) => ({ headers: { ...headers, authorization: `Bearer ${localStorage.getItem("token")}` }, })); const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors?.some(e => e.extensions?.code === "UNAUTHENTICATED")) { localStorage.removeItem("token"); window.location.href = "/login"; } }); const client = new ApolloClient({ link: ApolloLink.from([errorLink, authLink, httpLink]), cache: new InMemoryCache(), });
bashnpm install @apollo/client graphql
__typename:id; updates to one entity propagate everywhereoptimisticResponse for mutations; UI updates instantly, corrects on server responsetypePolicies for pagination merging, field read/write policiesgraphql-codegen to generate TypeScript types from your schema; fully typed queriesonError link for global error handling (auth refresh, logging, retry)update function or refetchQueries after mutations; prefer cache.modify for performancepollInterval for simple real-time, WebSocket subscriptions for true real-time| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | pass→pass | 10,651 | 9,966 | -6% | 1 | 1 | 0% | 2,140 | 3,240 | +51% | 0 | 0 | — |
case-03 | pass→pass | 9,405 | 8,912 | -5% | 1 | 1 | 0% | 1,686 | 3,083 | +83% | 0 | 0 | — |
case-04 | pass→pass | 13,244 | 9,243 | -30% | 1 | 1 | 0% | 2,091 | 2,909 | +39% | 0 | 0 | — |
case-01 | pass→pass | 12,044 | 14,177 | +18% | 1 | 1 | 0% | 2,465 | 3,624 | +47% | 0 | 0 | — |
case-05 | pass→pass | 10,234 | 13,319 | +30% | 1 | 1 | 0% | 1,936 | 3,825 | +98% | 0 | 0 | — |
case-06 | pass→pass | 8,083 | 8,116 | +0% | 1 | 1 | 0% | 1,532 | 2,721 | +78% | 0 | 0 | — |
case-07 | pass→pass | 11,474 | 11,431 | -0% | 1 | 1 | 0% | 2,129 | 3,267 | +53% | 0 | 0 | — |
case-08 | pass→fail | 2,503 | 4,015 | +60% | 1 | 1 | 0% | 444 | 1,888 | +325% | 0 | 0 | — |
case-09 | pass→pass | 6,894 | 8,103 | +18% | 1 | 1 | 0% | 1,064 | 2,534 | +138% | 0 | 0 | — |
case-10 | pass→pass | 11,940 | 8,472 | -29% | 1 | 1 | 0% | 1,809 | 2,784 | +54% | 0 | 0 | — |
case-11 | pass→pass | 3,693 | 4,725 | +28% | 1 | 1 | 0% | 620 | 1,978 | +219% | 0 | 0 | — |
case-12 | pass→pass | 11,164 | 9,979 | -11% | 1 | 1 | 0% | 2,085 | 3,076 | +48% | 0 | 0 | — |
case-13 | pass→pass | 16,307 | 12,047 | -26% | 1 | 1 | 0% | 2,563 | 3,434 | +34% | 0 | 0 | — |
case-14 | pass→pass | 14,512 | 9,883 | -32% | 1 | 1 | 0% | 1,742 | 3,060 | +76% | 0 | 0 | — |
case-15 | pass→pass | 7,071 | 6,972 | -1% | 1 | 1 | 0% | 1,314 | 2,249 | +71% | 0 | 0 | — |
case-16 | pass→pass | 8,992 | 7,581 | -16% | 1 | 1 | 0% | 1,666 | 2,473 | +48% | 0 | 0 | — |
case-17 | fail→pass | 8,246 | 8,963 | +9% | 1 | 1 | 0% | 1,499 | 3,015 | +101% | 0 | 0 | — |
case-18 | pass→pass | 6,773 | 7,315 | +8% | 1 | 1 | 0% | 1,041 | 2,457 | +136% | 0 | 0 | — |
case-19 | pass→pass | 10,935 | 9,713 | -11% | 1 | 1 | 0% | 2,087 | 3,009 | +44% | 0 | 0 | — |
case-20 | pass→pass | 10,212 | 8,897 | -13% | 1 | 1 | 0% | 2,027 | 2,956 | +46% | 0 | 0 | — |
case-21 | pass→pass | 6,018 | 7,326 | +22% | 1 | 1 | 0% | 1,041 | 2,540 | +144% | 0 | 0 | — |
case-22 | pass→pass | 10,805 | 12,166 | +13% | 1 | 1 | 0% | 2,057 | 3,521 | +71% | 0 | 0 | — |
case-23 | pass→pass | 9,950 | 11,551 | +16% | 1 | 1 | 0% | 2,317 | 3,633 | +57% | 0 | 0 | — |
case-24 | pass→pass | 10,034 | 11,075 | +10% | 1 | 1 | 0% | 2,056 | 3,297 | +60% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 24 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.