Next.js

Optimizing calls to DatoCMS

Next.js 15 and Later

Starting with Next.js 15, fetch is no longer auto-cached. It is now an opt-in mechanism. Please see the Next 15 caching docs for details.

Next.js 14

Although the Next.js fetch API has (almost) the same interface as the regular fetch available on the browser, it is important to highlight some key differences, which might cause some surprise.

Next.js 14 automatically caches fetches

By default, Next.js automatically caches your fetches:

  • For fetch calls happening in Server Components, this means that the data will be fetched at build time, cached, and reused indefinitely on each request until your next deploy.

  • For fetch calls happening in Client Components, the cache lasts the duration of a session (which could include multiple client-side re-renders) before a full page reload.

Caching requests is generally a good idea, as it minimizes the number of requests made to DatoCMS. However, if you want to always fetch the latest data, you can mark requests as dynamic and fetch data on each request without caching.

GraphQL calls need to be manually cached

The automatic caching only works for GET requests. Since GraphQL requests use a POST HTTP action, we need to manually handle CDA caching ourselves.

For this purpose, we can use a useful helper that React offers called cache, which memoizes the result of the passed function: Next.js: React Cache Function.

Our improved performRequest

Based on what we have just learned, we can refine our performRequest function, and make it more flexible and optimized:

import { executeQuery } from '@datocms/cda-client';
import { cache } from 'react';
const dedupedPerformRequest = cache(async (serializedArgs) => {
return executeQuery(...JSON.parse(serializedArgs));
})
export function performRequest(query, options) {
return dedupedPerformRequest(JSON.stringify([
query,
{
...options,
token: process.env.NEXT_DATOCMS_API_TOKEN,
environment: process.env.NEXT_DATOCMS_ENVIRONMENT,
},
]);
}

This new version dedupes your GraphQL requests, supports all CDA header modes, and lets you control if — and for how long — you want to cache the result of the query with the revalidate option:

// cache the query result indefinitely (until next deploy)
await performRequest(query);
// cache the query result for a maximum of 60 seconds
await performRequest(query, requestInitOptions: { next: { revalidate: 60 } });

Last updated: November 5th, 2025