Next.js Incremental Static Site Regeneration and Apollo GraphQL Caching

Friday, March 12, 2021

I’ve recently been working on a project built using Next.js & Apollo GraphQL to pull data out of Sitecore Experience Edge for Content Hub. I’ve really enjoyed working with these technologies but ran into something that stumped me for a while the other day.

I was testing the Incremental Static Regeneration (ISR) functionality in Next.Js, this allows for a partial regeneration of the static pages when some of the content changes. This is really helpful when pulling data from an external system like Sitecore Experience Edge for Content Hub. In this scenario if one piece of content changes you don’t want to republish all of your static pages as that could be really heavy, you just want to refresh the ones that have changed. This is where ISR comes in as it allows you to do just that, you can read more about how to configure it here.

The issue I ran into was that my content wasn’t refreshing on my page when I had changed it in Content Hub and the revalidate period had expired. If I stopped and started the project on my development machine, I would see the change, but the static page would never be regenerated through ISR.

Homer Thinking

After a lot of head scratching I figured out that this was due to the InMemoryCache configured by default when working with Apollo GraphQL. You can read more about how that is configured here. By default Apollo will cache any GQL queries, so if it receives the same query again it will just return the same result as previously. Now this might be a good idea in a lot of cases, when it comes to ISR this means that our static pages will never be regenerated!

Picard facepalm

So how do we fix this? Well it turns out that when you create your ApolloClient you can specify a set of DefaultOptions and these include the caching policies for different GQL requests. It was a simple matter of turning caching off for queries by using the following code…..

export const client = new ApolloClient({
    link: from([
        authMiddleware,
        httpLink
    ]),
    cache: new InMemoryCache(),
    defaultOptions: {
        query: {
            fetchPolicy: 'no-cache',
        },
    }
});

Here you can see that the fetchPolicy for all query requests is now set to no-cache. After enabling this my ISR started working just as it should! Victory!

Rocky Victory