Search code examples
cachinggraphqlapolloreact-apolloapollo-client

Cache GraphQL query with multiple ids using Apollo


we're using apollo client in a web application and are looking for ways to improve cache usage.

We have a query that takes an array of ids as parameter an example query with ids foo and bar would look like this:

query routes {
  routes(routeNames: ["foo", "bar"]) {
    items {
      name
      route
      defaults
    }
  }
}

The cache setup looks like this:

export const cacheRedirects = {
  Query: {
    routes: (_: any, args: RoutesArgs, { getCacheKey }: Resolver<'name'>): Array<CacheKey> =>
      args.routeNames.map(name => getCacheKey({ __typename: 'Route', name })),
  },
};

export const dataIdFromObject = (object: QueryResult): ?string => {
  switch (object.__typename) {
    case 'Route':
      return `${object.__typename}:${object.name}`;
    default: return defaultDataIdFromObject(object);
  }
};

export function newCache(): InMemoryCache {
  return new InMemoryCache({ dataIdFromObject, cacheRedirects });
}

Now when using the query in several places in our client we'd like to fetch only data for routeNames not cached via network and retrieve the rest via cache.

So the problem boils down to this: When having one query that caches the results for routeNames: ["foo", "bar"] and later another query comes along asking for the routes for routeNames: ["bar", "baz"] we'd love to take the result corresponding to "bar" from cache and send a query for routeNames: ["baz"].

I'm uncertain whether and how this can be done with Apollo because in contrast to the cacheRedirect example here we deal with multiple ids rather than a single one.


Now if we can't cache per array item the next best thing we could do would be to transform the ids into common cache keys so that ["foo", "bar"] and ["bar", "foo"] end up using the same cache key, but ["foo", "baz"] would use a different one.

Of course the ideal thing would be to only fetch "baz" as the missing item in our scenario.


Solution

  • One idea is to check the local cache before making the actual query, and simply drop the IDs (routeNames) already in the cache from the list of IDs (routeNames) to query.

    To check the cache without touching the network, use readQuery() or readFragment().

    The documentation is here:

    https://www.apollographql.com/docs/react/caching/cache-interaction#readquery https://www.apollographql.com/docs/react/caching/cache-interaction#readfragment

    UPDATE: Alternatively you can also set the fetchPolicy to cache-only in your query options.

    https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy

    UPDATE 2: A nice way to integrate the checks so they run for every query may be a custom client middleware, which will be run on every request before calling the server.

    https://www.apollographql.com/docs/react/api/link/introduction/