Have you ever wondered how to perform data cache using function other than the modified Next.js fetch()? The unstable_cache() function is an experimental API to cache function calls and cache the result to the Data Cache when the fetch API is not suitable.

Why Data Cache

In the past, caching data in between requests can only be done either by opting into static generation or using fetch(). Either you build the route once at request time and cache the build result (called Build Cache) for subsequent requests or re-render routes at every request which can take a long time to finish. Some times later, you found out that you can cache fetch results so that dynamic route rendering does not take that long since you can just use the data that are fetched not so long ago.

Untitled

However, caching data results that persists in between render requests can only be done with the fetch() function which meant that any function you want to cache needs to be from an external endpoint. Of course you can just create an API route and fetch the result on that route instead but that would break the “no fetching own endpoint” anti-pattern.

unstable_cache()

In order to cache data to persist in between requests we can use unstable_cache() and use the Data Cache. The result of the function wrapped with unstable_cache() will be cached into the Data Cache just like a server-side fetch() would.

import { unstable_cache } from 'next/cache'
 
export default async function Page() {
  const cachedData = await unstable_cache(
    async () => {
      const data = await db.query('...')
      return data 
    } // "data" will be cached for subsequent request
  )()
}

gotchas #1

gotchas #2

Cache Key

The Cache key is used to differentiate one function from another. By default the cache key is computed by concatenating the callback’s source code and the argument that is passed to the callback function. However, additional identifiers/label can be added to make sure that the function are different from each other to avoid one of the common pitfalls. These labels are called keyParts

gotchas #3

When using variables outside of the callback function one should always include those variables in the cache key array just like how one use the dependency array in useEffect. The cache key should be treated as a dependency array to prevent pulling the cache when the external variables had changed.

The example below would pull up the wrong cache when user access page with id 2 right after accessing page with id of 1 since both page with id 1 & 2 have the same cache key. In this case, params.id is an external variable and should be treated as a dependency

// ❌ wrong cache is retrieved when params.id changes
export default async function Page({ params }) {
  const cachedData = await unstable_cache(
    async () => await getData(params.id), // ※
    []
  )()
}

Therefore, really make sure that external variables are added in the cache key or the argument of the parameters so that unstable_cache can correctly get the correct cache.