Defining type policies during runtime in Apollo

Apollo InMemoryCache type policies are used to define field policies for local state, to customize identifier generation by type or to define a custom merge function for the cache.

The standard way of doing this is by defining the type policies during initialization of apollo client like this:

const apolloClient = new ApolloClient({
    ...
    cache: new InMemoryCache({
        typePolicies: {
            // defining field policy for local state
            Article: {
             // Field policy map for the Article type
                fields: {
                    // Field policy for the isBookmarked field
                    isBookmarked: {
                        // The read function for the isBookmarked field
                        read(_, { variables }) {
                            return localStorage
                              .getItem('BOOKMARKS')
                              .includes(
                                 variables.articleId
                              );
                        }
                    }
                }
            },

            // customizing identifier generation
            Book: {
                keyFields: ["isbn"],
            }


            // defining custom merge function
            SearchResults: {
                fields: {
                    results: {
                        merge(existing = [], incoming: any[]) {
                            return [...existing, ...incoming];
                        },
                    }
                }
            }
        },
    });
})

This might not always be possible. You might want to do this at runtime instead. Some of these scenarios:

  1. If you are loading different chunks at runtime based on user interaction
  2. You have a microfrontends architecture (like in our case).

To do this you can follow these steps:

1. Get an instance of the cache

const { loading, error, data, client } = useQuery(QUERY);

const cache = client.cache

2. Add policies to the cache

To specify key fields for custom identifier generation

cache.policies.addTypePolicies({
    // customizing identifier generation
    Book: {
        keyFields: ["isbn"],
    }
})

Or to define field policy for local storage

cache.policies.addTypePolicies({
    Article: {
        fields: { 
            isBookmarked: { 
                read(_, { variables }) { 
                    return localStorage
                      .getItem('BOOKMARKS')
                      .includes(
                        variables.articleId
                      );
                }
            }
        }
    },
})

Caveat

The reason why it is recommended to configure these at build time is because that way apollo can ensure that the data is not accessed before the type policies are defined. If you want to do this at runtime, you need to make sure that you call addTypePolicies before you call useQuery with the query that uses the type whose policy you are modifying