Apollo Client Local and Remote State Management

Apollo Client functions as a comprehensive state management solution for JavaScript applications, unifying remote data from GraphQL APIs and local client state into a single cohesive architecture. By leveraging an in-memory normalized cache, reactive variables, and client-side field policies, Apollo Client eliminates the need for separate state management libraries like Redux or Zustand. This article explores the mechanisms Apollo Client uses to fetch, cache, and synchronize remote data while seamlessly handling local-only application state.

Remote State Management

Remote state refers to data stored on a server that the client application fetches, displays, and modifies. Apollo Client handles this lifecycle automatically through its caching layer and declarative query mechanism.

Normalized In-Memory Cache

At the core of Apollo Client’s remote state management is the InMemoryCache. When a GraphQL query returns data, the cache normalizes it through three main steps:

  1. Splitting Objects: The cache identifies individual objects within the nested response using a unique identifier (by default, __typename combined with an id or _id field).
  2. Flattening the Tree: Objects are stored as a flat lookup table of entities rather than deeply nested query results.
  3. Deduplicating Data: If multiple queries request the same entity, it is stored only once in the cache.

When a mutation updates an existing entity containing the matching identifier, Apollo Client updates the normalized record automatically, triggering an immediate re-render across all UI components listening to that data.

Fetch Policies

Apollo Client uses fetch policies to control how queries interact with the local cache and the network:


Local State Management

Local state refers to client-only data, such as UI theme preferences, form draft states, or modal visibility flags. Apollo Client provides two primary tools to manage local state: Reactive Variables and Field Policies.

Reactive Variables

Reactive variables (makeVar) allow developers to store local state outside the Apollo Client cache while still triggering UI updates. They are lightweight, require no GraphQL schema definitions, and can be read or modified anywhere in the application.

import { makeVar } from '@apollo/client';

// Create a reactive variable
export const isDarkModeVar = makeVar(false);

// Read the variable
const isDark = isDarkModeVar();

// Update the variable
isDarkModeVar(true);

When a component reads a reactive variable using the useReactiveVar hook, it automatically re-renders whenever the variable value changes.

Field Policies and the @client Directive

For applications that prefer managing local state entirely through GraphQL operations, Apollo Client supports Type Policies and the @client directive.

Developers define custom read and merge functions inside the InMemoryCache configuration:

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        isDarkMode: {
          read() {
            return isDarkModeVar();
          },
        },
      },
    },
  },
});

Clients can then request this field alongside remote fields in a standard GraphQL query by applying the @client directive, signaling to Apollo Client that the field should be resolved locally rather than sent to the backend server.


The Unified Graph Architecture

The primary advantage of Apollo Client’s state model is the unified data graph. Developers can execute a single query that retrieves both remote server data and local UI state simultaneously:

query GetUserProfile {
  user(id: "123") {
    name
    email
  }
  isDarkMode @client
  cartItemCount @client
}

Apollo Client strips out the @client fields before sending the network request, retrieves the remote data, resolves the local fields via defined policies or reactive variables, and returns a single merged result to the component. This design minimizes boilerplate, consolidates debugging through Apollo DevTools, and provides a single, consistent API for all application state.