Apollo Client 3.0 Cache with Namespaced Queries

Viewed 369

I have some questions about how to fit apollo client cache together with a specific way we are organizing our queries.

So the way we organize our queries is that we group queries on the same resource under a single namespace. E.g. we have two resources, users and accounts, and our queries look like this.

type Query {
  user: UserQueries
  account: AccountQueries
}

type UserQueries {
  all: [User!]!
  byId(id: ID!): User
}

type AccountQueries {
  all: [Account!]!
  byId(id: ID!): Account
}

type User {...}
type Account {...}

Since neither Queries type provides an ID field, by default the response from a second query under UserQueries will replace the data from the first query in the cache, i.e. first I make an all query, and I'll get back some data that gets stored in the cache under the user field. And then if I make a byId query, the data in the cache under user will get replaced by the new data.

Some questions about this process:

  1. I noticed that even after the all data is replaced by the byId data, the previously fetched users (the normalized ones) are still available in the cache, when I thought they would be garbage collected since the references are lost. Does the garbage collector not garbage collect them immediately, but rather run on a certain cadence? Or am I completely misunderstanding how gc works?
  2. Is there another way, other than generating a unique ID for the query typesuser, to preserve both responses?
  3. Is it even a good idea to namespace queries like this?

Thanks folks!

2 Answers

As you have experienced, the results will be overwritten by a different query because the cache does not know that the query technically always returns the same object.

There are two solutions to this:

You have already identified the first one: Give these objects a static ID field. This is pretty ugly. The second one is to write merge functions for these query fields as discussed in the docs. I don't think namespaces are a bad idea in general, but as you can see, you would have to write this merge function for basically any entity type in your schema. Therefore, I would suggest to use namespaces sparingly.

The garbage collector also got some updates in Apollo 3. It can now be customised and run at any time. You can read more about the GC here. Objects that are no longer referenced might stay in the cache for some time. The docs don't seem to specify when the GC runs. It can be manually run by calling client.gc().

Actually, the right way to use namespaced queries with Apollo 3 is setting keyFields as an empty array. When you do that, you show to Apollo that there is no id for merging, so everything should be merged in the same cache key. You don't have to write merge functions for this.

It is configured through typePolicies option at InMemoryCache configuration

const apolloClient = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      UserQueries: {
        keyFields: [],
      },
      AccountQueries: {
        keyFields: [],
      },
    },
  }),
});
Related