Claude
Skills
Sign in
Back

apollo-caching-strategies

Included with Lifetime
$97 forever

Use when implementing Apollo caching strategies including cache policies, optimistic UI, cache updates, and normalization.

Design

What this skill does


# Apollo Caching Strategies

Master Apollo Client's caching mechanisms for building performant applications
with optimal data fetching and state management strategies.

## Overview

Apollo Client's intelligent cache is a normalized, in-memory data store that
allows for efficient data fetching and updates. Understanding cache policies
and management strategies is crucial for building high-performance apps.

## Installation and Setup

### Cache Configuration

```javascript
// apollo/cache.js
import { InMemoryCache, makeVar } from '@apollo/client';

export const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        posts: {
          // Pagination with offset
          keyArgs: ['filter'],
          merge(existing = [], incoming, { args }) {
            const merged = existing.slice(0);
            const offset = args?.offset || 0;

            for (let i = 0; i < incoming.length; i++) {
              merged[offset + i] = incoming[i];
            }

            return merged;
          }
        }
      }
    },
    Post: {
      keyFields: ['id'],
      fields: {
        comments: {
          merge(existing = [], incoming) {
            return [...existing, ...incoming];
          }
        }
      }
    },
    User: {
      keyFields: ['email'],
      fields: {
        fullName: {
          read(_, { readField }) {
            return `${readField('firstName')} ${readField('lastName')}`;
          }
        }
      }
    }
  }
});
```

## Core Patterns

### 1. Fetch Policies

```javascript
// Different fetch policies for different use cases
import { useQuery } from '@apollo/client';
import { GET_POSTS } from './queries';

// cache-first (default): Check cache first, network if not found
function CacheFirstPosts() {
  const { data } = useQuery(GET_POSTS, {
    fetchPolicy: 'cache-first'
  });
  return <PostsList posts={data?.posts} />;
}

// cache-only: Never make network request, cache or error
function CacheOnlyPosts() {
  const { data } = useQuery(GET_POSTS, {
    fetchPolicy: 'cache-only'
  });
  return <PostsList posts={data?.posts} />;
}

// cache-and-network: Return cache immediately, update with network
function CacheAndNetworkPosts() {
  const { data, loading, networkStatus } = useQuery(GET_POSTS, {
    fetchPolicy: 'cache-and-network',
    notifyOnNetworkStatusChange: true
  });

  return (
    <div>
      {networkStatus === 1 && <Spinner />}
      <PostsList posts={data?.posts} />
    </div>
  );
}

// network-only: Always make network request, update cache
function NetworkOnlyPosts() {
  const { data } = useQuery(GET_POSTS, {
    fetchPolicy: 'network-only'
  });
  return <PostsList posts={data?.posts} />;
}

// no-cache: Always make network request, don't update cache
function NoCachePosts() {
  const { data } = useQuery(GET_POSTS, {
    fetchPolicy: 'no-cache'
  });
  return <PostsList posts={data?.posts} />;
}

// standby: Like cache-first but doesn't auto-update
function StandbyPosts() {
  const { data, refetch } = useQuery(GET_POSTS, {
    fetchPolicy: 'standby'
  });

  return (
    <div>
      <button onClick={() => refetch()}>Refresh</button>
      <PostsList posts={data?.posts} />
    </div>
  );
}
```

### 2. Cache Reads and Writes

```javascript
// apollo/cacheOperations.js
import { gql } from '@apollo/client';

// Read from cache
export function readPostFromCache(client, postId) {
  try {
    const data = client.readQuery({
      query: gql`
        query GetPost($id: ID!) {
          post(id: $id) {
            id
            title
            body
          }
        }
      `,
      variables: { id: postId }
    });
    return data?.post;
  } catch (error) {
    console.error('Post not in cache:', error);
    return null;
  }
}

// Write to cache
export function writePostToCache(client, post) {
  client.writeQuery({
    query: gql`
      query GetPost($id: ID!) {
        post(id: $id) {
          id
          title
          body
        }
      }
    `,
    variables: { id: post.id },
    data: { post }
  });
}

// Read fragment
export function readPostFragment(client, postId) {
  return client.readFragment({
    id: `Post:${postId}`,
    fragment: gql`
      fragment PostFields on Post {
        id
        title
        body
        likesCount
      }
    `
  });
}

// Write fragment
export function updatePostLikes(client, postId, likesCount) {
  client.writeFragment({
    id: `Post:${postId}`,
    fragment: gql`
      fragment PostLikes on Post {
        likesCount
      }
    `,
    data: {
      likesCount
    }
  });
}

// Modify cache fields
export function incrementPostLikes(client, postId) {
  client.cache.modify({
    id: client.cache.identify({ __typename: 'Post', id: postId }),
    fields: {
      likesCount(currentCount = 0) {
        return currentCount + 1;
      },
      isLiked() {
        return true;
      }
    }
  });
}
```

### 3. Optimistic Updates

```javascript
// components/OptimisticLike.js
import { useMutation } from '@apollo/client';
import { LIKE_POST } from '../mutations';

function OptimisticLike({ post }) {
  const [likePost] = useMutation(LIKE_POST, {
    variables: { postId: post.id },

    // Optimistic response
    optimisticResponse: {
      __typename: 'Mutation',
      likePost: {
        __typename: 'Post',
        id: post.id,
        likesCount: post.likesCount + 1,
        isLiked: true
      }
    },

    // Update cache
    update(cache, { data: { likePost } }) {
      cache.modify({
        id: cache.identify(post),
        fields: {
          likesCount() {
            return likePost.likesCount;
          },
          isLiked() {
            return likePost.isLiked;
          }
        }
      });
    },

    // Handle errors
    onError(error) {
      console.error('Like failed, reverting:', error);
      // Optimistic update automatically reverted
    }
  });

  return (
    <button onClick={() => likePost()}>
      {post.isLiked ? 'Unlike' : 'Like'} ({post.likesCount})
    </button>
  );
}

// Complex optimistic update with multiple changes
function OptimisticCreateComment({ postId }) {
  const [createComment] = useMutation(CREATE_COMMENT, {
    optimisticResponse: ({ body }) => ({
      __typename: 'Mutation',
      createComment: {
        __typename: 'Comment',
        id: `temp-${Date.now()}`,
        body,
        createdAt: new Date().toISOString(),
        author: {
          __typename: 'User',
          id: currentUser.id,
          name: currentUser.name,
          avatar: currentUser.avatar
        }
      }
    }),

    update(cache, { data: { createComment } }) {
      // Add comment to post
      cache.modify({
        id: cache.identify({ __typename: 'Post', id: postId }),
        fields: {
          comments(existing = []) {
            const newCommentRef = cache.writeFragment({
              data: createComment,
              fragment: gql`
                fragment NewComment on Comment {
                  id
                  body
                  createdAt
                  author {
                    id
                    name
                    avatar
                  }
                }
              `
            });
            return [...existing, newCommentRef];
          },
          commentsCount(count = 0) {
            return count + 1;
          }
        }
      });
    }
  });

  return <CommentForm onSubmit={createComment} />;
}
```

### 4. Cache Eviction

```javascript
// apollo/eviction.js
export function evictPost(client, postId) {
  // Evict specific post
  client.cache.evict({
    id: client.cache.identify({ __typename: 'Post', id: postId })
  });

  // Garbage collect
  client.cache.gc();
}

export function evictField(client, postId, fieldName) {
  // Evict specific field
  client.cache.evict({
    id: client.cache.identify({ __typename: 'Post', id: postId }),
    fieldName
  });
}

export function evictAllPosts(client) {
  // Evict all posts from cache
  client.cache.modify({
    fields: {
      po

Related in Design