Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apollo Client - using cached results from object list in response to query for single object

Is it possible to configure the Apollo Client to fetch a single cached Item from a query that returns a list of Items, in order to prefetch data when querying for a single Item?

Schema:

type Item {
  id: ID!
  name: String!
}

type Query {
  items: [Item!]!
  itemById(id: ID!): Item!
}

Query1:

query HomepageList {
  items {
    id
    name
  }
}

Query2:

query ItemDetail($id: ID!) {
  itemById(id: $id) {
    id
    name
  }
}

Given that the individual Item's data will already be in the cache, it should be possible to use the already cached data whilst still executing a fetch incase any data has changed.

However, the query does not utilise the cached data (by default at least), and it seems that we need to somehow tell Apollo that we know the Item is already in the cache.

Any help greatly appreciated.

like image 910
cjpete Avatar asked Jan 22 '21 09:01

cjpete


People also ask

Does Apollo Client cache?

Overview. Apollo Client stores the results of your GraphQL queries in a local, normalized, in-memory cache. This enables Apollo Client to respond almost immediately to queries for already-cached data, without even sending a network request. The Apollo Client cache is highly configurable.

How do you use Apollo Client to fetch data?

You pass a query object to ApolloClient#fetch(query:) to send the query to the server, execute it, and receive typed results. By default, Apollo will deliver query results on the main thread, which is probably what you want if you're using them to update the UI.

What does a GraphQL Client usually do before caching the results of a query?

Generally, when caching data, the intuition is to put information that's fetched remotely into a local store from where it can be retrieved later on. With GraphQL, the naive approach would be to simply put the results of GraphQL queries into the store and simply return them whenever the same query is sent.

How do you persist the Apollo cache?

Persisting the cache You can persist and rehydrate the InMemoryCache from a storage provider like AsyncStorage or localStorage . To do so, use the apollo3-cache-persist library. This library works with a variety of storage providers. To get started, pass your cache and a storage provider to persistCache .


1 Answers

This functionality exists, but it's hard to find if you don't know what you're looking for. In Apollo Client v2 you're looking for cache redirect functionality, in Apollo Client v3 this is replaced by type policies / field read policies (v3 docs).

Apollo doesn't 'know' your GraphQL schema and that makes it easy to set up and work with in day-to-day usage. However, this implies that given some query (e.g. getBooks) it doesn't know what the result type is going to be upfront. It does know it afterwards, as long as the __typename's are enabled. This is the default behaviour and is needed for normalized caching.

Let's assume you have a getBooks query that fetches a list of Books. If you inspect the cache after this request is finished using Apollo devtools, you should find the books in the cache using the Book:123 key in which Book is the typename and 123 is the id. If it exists (and is queried!) the id field is used as identifier for the cache. If your id field has another name, you can use the typePolicies of the cache to inform Apollo InMemoryCache about this field.

If you've set this up and you run a getBook query afterwards, using some id as input, you will not get any cached data. The reason is as described before: Apollo doesn't know upfront which type this query is going to return.

So in Apollo v2 you would use a cacheRedirect to 'redirect' Apollo to the right cache:

  cacheRedirects: {
    Query: {
      getBook(_, args, { getCacheKey }) {
        return getCacheKey({
          __typename: 'Book',
          id: args.id,
        });
      }
    },
  },

(args.id should be replaced by another identifier if you have specified another key in the typePolicy)

When using Apollo v3, you need a typepolicy / field read policy:

  typePolicies: {
    Query: {
      fields: {
        getBook(_, { args, toReference }) {
          return toReference({
            __typename: 'Book',
            id: args.id,
          });
        }
      }
    }
  }
like image 178
Bram Avatar answered Oct 26 '22 22:10

Bram