Skip to content

Instantly share code, notes, and snippets.

@tamlyn
Created October 13, 2022 08:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tamlyn/f6e6ed49401546a03502d477b1bf562e to your computer and use it in GitHub Desktop.
Save tamlyn/f6e6ed49401546a03502d477b1bf562e to your computer and use it in GitHub Desktop.

How the Apollo Client cache works

Queries

A query is only served from cache if the same query path, with the same variables, was previously requested.

For example if you fetch all Fund objects:

query {
  funds {
    id
    name
  }
}

Then it will store each fund in the cache in a normalised form using (by default) the __typename and id properties: Fund:1, Fund:2, etc.

If you then fetch a single fund:

query {
  fund(id: 1) {
    id
    name
    isin
  }
}

That will be a cache miss even though the Fund:1 is in the cache. That's because Apollo doesn't know how the server will handle the id argument – it could return anything.

But if you then request:

query {
  fund(id: 1) {
    id
    name
  }
}

Even though this exact query has not been run before (it doesn't request isin), it is a cache hit because Apollo knows that the query previously returned Fund:1 and it has all the requested fields in the cache.

On the other hand:

query {
  fund(id: 1) {
    id
    name
    market
  }
}

Would be a cache miss because it doesn't have the market property.

Mutations

Apollo Client doesn't make any assumptions about what mutations do, but it does cache the return values just the same as for queries.

So if you were to request:

mutation {
  setFundName(name: "New name") {
    id
    name
  }
}

Then Apollo would update the cache with whatever the mutation returned such that a future execution of:

query {
  fund(id: 1) {
    id
    name
  }
}

Would return the new name.

That's why it's really important to always query the ID of a type and for mutations to query any fields which they change.

No ID

Remember that every object is a type in GraphQL:

query {
  user {
    id
    nationalInsuranceNumber
    address {
      addressLine1
      postcode
    }
  }
}

Here the nested address is its own type. But it has no id property so it can't be cached under its own key. Instead Apollo caches it inside the associated user object.

But that has implications for mutations:

mutation {
  setAddress(address: { addressLine1: "New St", postcode: "A1" }) {
    addressLine1
    postcode
  }
}

Because the address has no ID, Apollo can't update the cache. Which means the user query above has a stale address.

There are ways to tell Apollo Client to refetch queries in the background, but it's better to use IDs whenever possible.

Note that the ID type has no effect on caching behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment