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 important differences, which might cause some surprise.
By default, Next.js automatically does static 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.
It's quite common that multiple elements in the hierarchy of "things" that gets evaluated by Next.js to build a server response — layouts, pages, server components and generateMetadata
/generateStaticParams
functions — need to perform the same query.
Next.js automatically handles request deduping on GET
requests — making sure that only one request is sent to the server — but since GraphQL requests use a POST
HTTP action, we need to manually handle the case ourselves.
For this purpose, we can use a useful helper that React offers called cache
, which memoizes the result of the passed function.
performRequest
Based on what we have just learned, we can resume our performRequest
function, and make it more flexible and optimized:
import { cache } from 'react';const dedupedFetch = cache(async (serializedInit) => {const response = await fetch("https://graphql.datocms.com/", JSON.parse(serializedInit));const responseBody = await response.json();if (!response.ok) {throw new Error(`${response.status} ${response.statusText}: ${JSON.stringify(responseBody)}`);}return responseBody;})export async function performRequest({query,variables = {},includeDrafts = false,excludeInvalid = false,visualEditingBaseUrl,revalidate,}) {const { data } = await dedupedFetch(JSON.stringify({method: "POST",headers: {Authorization: `Bearer ${process.env.NEXT_DATOCMS_API_TOKEN}`,...(includeDrafts ? { "X-Include-Drafts": "true" } : {}),...(excludeInvalid ? { "X-Exclude-Invalid": "true" } : {}),...(visualEditingBaseUrl ? { "X-Visual-Editing": "vercel-v1", "X-Base-Editing-Url": visualEditingBaseUrl } : {}),...(process.env.NEXT_DATOCMS_ENVIRONMENT ? { "X-Environment": process.env.NEXT_DATOCMS_ENVIRONMENT } : {}),},body: JSON.stringify({ query, variables, revalidate }),next: { revalidate },}));return data;}
This new version dedupes 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 secondsawait performRequest({ query, revalidate: 60 });// prevent the query result from being cached (always perform a request)await performRequest({ query, revalidate: 0 });