Search code examples
apolloapollo-client

How to configure apollo cache to uniquely identify a child elements based on their parent primary key


What is the proper way to configure apollo's cache normalization for a child array fields that do not have an ID of their own but are unique in the structure of their parent?

Let's say we have the following schema:

type Query {
  clients: [Client!]!
}

type Client {
  clientId: ID
  name: String!
  events: [Events!]!
}

type Events {
  month: String!
  year: Int!
  day: Int!
  clients: [Client!]!
}

At first I thought I can use multiple keyFields to achieve a unique identifier like this:

const createCache = () => new InMemoryCache({
  typePolicies: {
    Event: {
      keyFields: ['year', 'month', 'name'],
    },
  },
});

There would never be more than 1 event per day so it's safe to say that the event is unique for a client based on date

But the created cache entries lack a clientId (in the cache key) so 2 events that are on the same date but for different clients cannot be distinguished

Is there a proper way to configure typePolicies for this relationship?

For example the key field can be set to use a subfield:

const cache = new InMemoryCache({
  typePolicies: {
    Book: {
      keyFields: ["title", "author", ["name"]],
    },
  },
});

The Book type above uses a subfield as part of its primary key. The ["name"] item indicates that the name field of the previous field in the array (author) is part of the primary key. The Book's author field must be an object that includes a name field for this to be valid.

In my case I'd like to use a parent field as part of the primary key


Solution

  • If you can't add a unique event id, then the fallback is to disable normalization:

    Objects that are not normalized are instead embedded within their parent object in the cache. You can't access these objects directly, but you can access them via their parent.

    To do this you set keyFields to false:

    const createCache = () => new InMemoryCache({
      typePolicies: {
        Event: {
          keyFields: false
        },
      },
    });
    

    Essentially each Event object will be stored in the cache under its parent Client object.