Large Offline Datasets with Apollo Client
Exploring an example of using Apollo Client with large offline datasets.

The Problem
We are building a web application interacting with a GraphQL API. We are using the React and Apollo Client libraries; a fairly common scenario these days. The twist is that we have a large dataset that we need the web application to access while offline.
The Solution
This solution, unlike apollo-cache-persists, completely by-passes the in-memory cache; this is particularly relevant for large datasets.
First, we can use IndexedDB, supported by all major browsers, to store the dataset:
IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. While Web Storage is useful for storing smaller amounts of data, it is less useful for storing larger amounts of structured data. IndexedDB provides a solution.
— MDN — IndexedDB API
Second, we can use Apollo Client’s Local state management feature to access the dataset:
We’ve learned how to manage remote data from our GraphQL server with Apollo Client, but what should we do with our local data? We want to be able to access boolean flags and device API results from multiple components in our app, but don’t want to maintain a separate Redux or MobX store. Ideally, we would like the Apollo cache to be the single source of truth for all data in our client application.
— Apollo Client — Local State Management
note: While not addressed in this article, we would also use a web worker, supported by all major browsers, to handle downloading the dataset.
The Example
To illustrate the core concepts, we will walk through a simplified example application. The application is available for download.
The application first downloads and stores the dataset (a list of US cities) into IndexedDB.
Then, as the user enters text into the input, the application queries the dataset for cities that start with the text and displays the result.

Code Highlights
The first interesting bit of code provides the loadCities (stores the dataset into IndexedDB) and searchCities (queries the dataset) functions:
src/api/cities.ts
Observations:
- The application is written in TypeScript
- The JSON data is being loaded from a simulated API; data is stored in the application (imported JSON file) and a delay is used to simulate an asynchronous load
- The application uses the Dexie.js library to simplify the interface to IndexedDB
The next bit of interesting code are the files that define Apollo Client’s local state:
src/graphql/typeDefs.ts
src/graphql/defaults.ts
src/graphql/resolvers.ts
Observations:
- The interesting bit here is that one does not normally provide resolvers for Apollo Client’s local state queries; Apollo Client provides a default implementation that reads local state from the in-memory cache
The last bit of interesting code queries Apollo Client local state:
src/components/Cities/CitiesResults/index.tsx
Observations:
- By using
fetchPolicy="no-cache"
property we force Apollo Client into calling the query resolver - Also, this property also prevents Apollo Client from storing results in the in-memory cache (prevents filling up memory)
Wrap Up
Nothing terribly complicated here, just a simple example to what seemed like a complicated problem at the start.