TallerWebSolutions / apollo-cache-instorage

Apollo Cache implementation that facilitates locally storing resources
MIT License
96 stars 11 forks source link
apollo apollo-cache apollo-client cache persistence storage

Apollo InStorageCache

Build Status coverage npm version sponsored by Taller

apollo-cache-instorage is an extension to apollo-cache-inmemory that allows for granular cacheability of resources, in a storage of choice.

Purpose

The most famous implementation of a persistence layer for Apollo Client is apollo-cache-persist. The main caveats with that project is the fastly growing size of the cache, and the incapability of chosing what needs and needs not to be cached. apollo-cache-instorage solves that, while reducing the complexity on the setup and limiting interaction points between the caching solution and the Apollo Client multiple services.

Installation

yarn add apollo-cache-instorage

Usage

InStorageCache is an extension of InMemoryCache, so initialization is not so different than the other:

import { InStorageCache } from 'apollo-cache-instorage'
import { HttpLink } from 'apollo-link-http'
import ApolloClient from 'apollo-client'

const cache = new InStorageCache({
  storage: window.localStorage,
})

const client = new ApolloClient({
  link: new HttpLink(),
  cache,
})

Configuration

The InStorageCache constructor takes a config object with all the options available for InMemoryCache plus the following customization properties:

name type default required
storage Object true
shouldPersist Function () => true false
normalize Function JSON.stringify false
denormalize Function JSON.parse false
prefix String '' false

storage

A Web Storage complient storage implementation.

shouldPersist

shouldPersist(
  operation: String,
  dataId: String,
  value: ?Object
)

Callback to determine if a given data object should be cached. Takes three arguments:

normalize

normalize(
  value: Object
)

Normalization executed against a data object before attaching to the storage for persistence. Defaults to JSON.stringify.

denormalize

Denormalization executed against a persisted data after retrieving from the storage. Defaults to JSON.parse.

prefix

A prefix to use when persisting data to the storage. Useful for cases when the storage is shared between other application needs.

@persist directive

To facilititate persistance opt-in, this package also provides a mechanism to identify parts of a query that should be persisted using a @persist directive. To enable that, you must:

  1. Configure InStorageCache with an extra key addPersistField set to true;
  2. Use a provided special implementation of shouldPersist;
  3. Add the PersistLink to the chain of links.
import { ApolloLink } from 'apollo-link'
import { createHttpLink } from 'apollo-link-http'
import { InStorageCache, PersistLink } from 'apollo-cache-instorage'

const cache = new InStorageCache({
  addPersistField: true,
  shouldPersist: PersistLink.shouldPersist,
})

const link = ApolloLink.from([
  new PersistLink(),
  createHttpLink({ uri: '/graphql' }),
])

Then, you can mark query selections for persisting using the directive:

query SomeQuery {
  nonPersistingSelection {
    field
  }

  persistingSelection @persist {
    field
  }

  deepPersistingSelection {
    persistingSelection @persist {
      field
    }
  }
}

Caveats

ROOT_QUERY

Most of the cache consumption in Apollo starts off on the ROOT_QUERY special key. Make sure that if you implement shouldPersist you always allow the storage to persist the data related to this key, such as follows:

const shouldPersist = (operation, dataId, value) => {
  if (dataId === 'ROOT_QUERY') return true
  // ... other logic here.
}

cache.restore()

When restoring the cache (SSR hydration, for instance), keep in mind that any value inserted via hydrating will have precedence over the persisted data.