Cache
Framework Next.js 13 Fetching Cache
Whenever possible, we recommend fetching data inside Server Components
. Server Components always fetch data on the server. This allows you to:
backend data resources
(e.g. databases
).access tokens
and API keys
, from being exposed to the client.client
.client-server
waterfalls.By default, fetch will automatically fetch and cache data indefinitely.
fetch('https://...'); // cache: 'force-cache' is the default
To revalidate cached data at a timed interval, you can use the next.revalidate option in fetch() to set the cache lifetime of a resource (in seconds).
fetch('https://...', { next: { revalidate: 10 } });
See Revalidating Data for more information.
To fetch fresh data on every fetch request, use the cache: ’no-store’ option.
fetch('https://...', { cache: 'no-store' });
In the app directory
, you have additional options to explore:
loading.js
to show an instant loading state from the server while streaming in the result from your data fetching function.data fetching
lower in the component tree to only block rendering for the parts of the page that need it. For example, moving data fetching to a specific component
rather than fetching it at the root layout
.Whenever possible, it’s best to fetch data in the segment that uses it. This also allows you to show a loading state for only the part of the page that is loading, and not the entire page.
// layout.js|page.js
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
export function generateStaticParams(...)
you can use segment configuration to customize the cache behavior of the entire segment.
// app/page.tsx
import prisma from './lib/prisma';
export const revalidate = 3600; // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany();
return posts;
}
export default async function Page() {
const posts = await getPosts();
// ...
}
To minimize client-server waterfalls, we recommend this pattern to fetch data in parallel:
// app/artist/[username]/page.jsx
import Albums from './albums';
async function getArtist(username) {
const res = await fetch(`https://api.example.com/artist/${username}`);
return res.json();
}
async function getArtistAlbums(username) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`);
return res.json();
}
export default async function Page({ params: { username } }) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumsData = getArtistAlbums(username);
// Wait for the promises to resolve
const [artist, albums] = await Promise.all([artistData, albumsData]);
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums}></Albums>
</>
);
}
export default async function Page({ params: { username } }) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumData = getArtistAlbums(username);
// Wait for the artist's promise to resolve first
const artist = await artistData;
return (
<>
<h1>{artist.name}</h1>
{/* Send the artist information first,
and wrap albums in a suspense boundary */}
<Suspense fallback={<div>Loading...</div>}>
<Albums promise={albumData} />
</Suspense>
</>
);
}
// Albums Component
async function Albums({ promise }) {
// Wait for the albums promise to resolve
const albums = await promise;
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.name}</li>
))}
</ul>
);
}
API routes should still be defined in the pages/api/* directory
and NOT moved to the app directory
.
// pages/api/user.ts
import { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ name: 'John Doe' })
}