Skip to content

Cache Exchange

Normalized caching with automatic dependency tracking and fine-grained updates.

Basic Usage

typescript
import { createClient, cacheExchange, httpExchange } from '@mearie/react'; // or @mearie/vue, @mearie/svelte, @mearie/solid
import { schema } from '$mearie';

export const client = createClient({
  schema,
  exchanges: [cacheExchange(), httpExchange({ url: 'https://api.example.com/graphql' })],
});

How It Works

  • Normalize - Breaks down responses into entities (e.g., User:123)
  • Store - Saves entities in a normalized map
  • Track - Registers which queries depend on which entities
  • Invalidate - Updates only affected queries when data changes

Configuration

Fetch Policy

Set default caching behavior:

typescript
export const client = createClient({
  schema,
  exchanges: [
    cacheExchange({
      fetchPolicy: 'cache-first', // Default for all operations
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Available policies:

  • cache-first (default) - Use cache if available, otherwise fetch
  • cache-and-network - Return cached data immediately, then fetch and update
  • network-only - Always fetch from network, update cache
  • cache-only - Only use cache, throw error if not found

INFO

Mutations and subscriptions always use network-only fetch policy, regardless of the configured default.

You can override this per operation. See Per-Operation Fetch Policy.

Automatic Cache Updates

Cache automatically updates when mutations return data:

typescript
export const EditUser = ({ userId }: { userId: string }) => {
  const [updateUser] = useMutation(
    graphql(`
      mutation UpdateUser($id: ID!, $name: String!) {
        updateUser(id: $id, input: { name: $name }) {
          id
          name
        }
      }
    `),
  );

  const handleSubmit = async (name: string) => {
    await updateUser({ id: userId, name });
    // Cache automatically updates User:${userId} with new name
    // All components using this user re-render automatically
  };

  // ...
};

Per-Operation Fetch Policy

Override the default fetch policy for specific queries:

typescript
export const UserProfile = ({ userId }: { userId: string }) => {
  const { data } = useQuery(
    graphql(`
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          name
        }
      }
    `),
    { id: userId },
    { fetchPolicy: 'network-only' }, // Override default
  );

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

WARNING

Mutations and subscriptions always use network-only and cannot be overridden.

Progressive Enhancement

Cache is completely optional. Start without it:

typescript
export const client = createClient({
  schema,
  exchanges: [httpExchange({ url: 'https://api.example.com/graphql' })],
});

Add caching later with one line - components automatically benefit:

typescript
export const client = createClient({
  schema,
  exchanges: [cacheExchange(), httpExchange({ url: 'https://api.example.com/graphql' })],
});

Exchange Chain Placement

Place cacheExchange before httpExchange:

typescript
export const client = createClient({
  schema,
  exchanges: [
    dedupExchange(),
    retryExchange(),
    cacheExchange(), // Before HTTP
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

This ensures cache is checked before making network requests.

Next Steps