useQuery makes request instead taking data from cache

Hi folks,
we are working on ecommerce site using Shopify Storefront API and Apollo Client.

I have three operations:

  • mutation cartLinesAddWithoutCart - creates cart and adds the product, returns cart, and it’s id
  • mutation cartLinesAddWithCart - adds another product to cart by id, returns cart and it’s id
  • query getCart - returns cart by id

When I run my cartLinesAddWithoutCart mutation, I get cart back with its id. I would expect that would automatically provide data for getCart query. But instead, it executes getCart over the network, instead of reading it from cache, which is far from ideal.

When I run my cartLinesAddWithCart mutation, everything works perfectly fine. When I get a response, cart updates immediately.

What might I be missing here?

Reproduction: Queries > Example app final (forked) - CodeSandbox

You can see that after hitting Create cart & add, we do get back the id and then the query takes place. If you uncomment fetchPolicy on line 190 it will not work at all.

Hello, great question! This is one of the trickier nuances of the Apollo Client cache that I’m hoping to better clarify in the docs soon. To resolve your issue we can use cache redirects, which are documented here.

By default, the cache has no way to know that getCart should return the same Cart that was previously cached by cartLinesAddWithoutCart. To Apollo Client, these are two GraphQL operations that might have absolutely nothing to do with each other.

But! We can tell getCart how to check the cache for an existing Cart object so that it doesn’t need to hit the network. To do that, we can define a custom read function for Query.cart in our InMemoryCache constructor, like so:

const client = new ApolloClient({
  link,
  cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            cart: {
              read(cart, { args, toReference }) {
                if (!cart) {
                  // No cart associated with this query, but there still
                  // might be one elsewhere in the cache. Check!
                  return toReference({
                    __typename: 'Cart',
                    id: args.id,
                  });
                } else {
                  // There's already a cart associated with this query,
                  // so return it
                  return cart;
                }
              }
            }
          }
        }
    }
  })
});

With this read function, whenever Query.cart is queried, if that field doesn’t already have an associated cart object in the cache, it generates a reference to a Cart object that might be somewhere else in the cache (specifically, returned by cartLinesAddWithoutCart).

That reference consists of the Cart typename, plus the id that was provided to the original query as an argument. If the cache does indeed contain a Cart object that corresponds to that reference, it’s returned! And otherwise, Apollo Client knows there is no Cart with that id in the cache, so a network request is necessary (assuming you have a cache-first fetch policy).

I forked your sandbox and applied this read function here. Clicking Create cart and add now successfully updates the Cart component, even though the fetch policy is set to cache-only. The cart is being successfully fetched from the cache.

1 Like