From 1ace3e6588134c1f6fd8c4e33cae45189e7c4ee6 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 18:33:48 -0500 Subject: [PATCH 1/6] WIP --- .../02-data-fetching/01-fetching.mdx | 333 ++++++------ .../02-caching-and-revalidating.mdx | 141 ------ .../04-incremental-static-regeneration.mdx | 472 ++++++++++++++++++ .../10-deploying/index.mdx | 2 +- .../04-incremental-static-regeneration.mdx | 179 +------ 5 files changed, 625 insertions(+), 502 deletions(-) delete mode 100644 docs/02-app/01-building-your-application/02-data-fetching/02-caching-and-revalidating.mdx create mode 100644 docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx diff --git a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx index e9ef6d5ea1e78..3ead2b9ba4bdf 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx @@ -1,263 +1,230 @@ --- -title: Data Fetching -nav_title: Fetching +title: Data Fetching and Caching +nav_title: Data Fetching and Caching description: Learn best practices for fetching data on the server or client in Next.js. --- -Data fetching is a core part of any application. This page walks through best practices for fetching data using your preferred method. +
+ Examples -## Should I fetch data on the server or the client? +- [Next.js Commerce](https://vercel.com/templates/next.js/nextjs-commerce) +- [On-Demand ISR](https://on-demand-isr.vercel.app) +- [Next.js Forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) -Deciding whether to fetch data on the server or the client depends on the type of UI you're building. +
-For most cases, where you don't need real-time data (e.g. polling), you can fetch data on the server with [Server Components](/docs/app/building-your-application/rendering/server-components). There are a few benefits to doing this: +This guide will walk you through the basics of data fetching and caching in Next.js, providing practical examples and best practices. -- You can fetch data in a single server-round trip, reducing the number of network requests and client-server waterfalls. -- Prevent sensitive information, such as access tokens and API keys, from being exposed to the client (which would require an intermediate API route). -- Reduce latency by fetching data close to the source (if your application code and database are in the same region). -- Data requests can be [cached](/docs/app/building-your-application/data-fetching/caching-and-revalidating#caching) and [revalidated](/docs/app/building-your-application/data-fetching/caching-and-revalidating#revalidating-data). +Here's a minimal example of data fetching in Next.js: -However, server-side data fetching will cause the whole page to be re-rendered on the server. In cases where you need to mutate/revalidate smaller pieces of UI or continually fetch real-time data (e.g. live views), client-side data fetching might be better suited, as it'll allow you to re-render the specific piece of UI on the client. +```tsx +export default async function Page() { + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() + return ( + + ) +} +``` -There are 4 ways you can fetch data in Next.js: +This example demonstrates a basic server-side data fetch using the `fetch` API in an asynchronous React Server Component. -- [`fetch` API](#fetch-api) on the server. -- [ORMs or Database Clients](#orms-and-database-clients) on the server. -- [Route Handlers](#route-handlers) on the server, via the client. -- [Data Fetching Libraries](#data-fetching-libraries) on the client. +## Reference -## `fetch` API +- [`fetch`](https://nextjs.org/docs/app/api-reference/functions/fetch) +- React [`cache`](https://react.dev/reference/react/cache) +- Next.js [`unstable_cache`](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) -Next.js extends the native [`fetch` Web API](https://developer.mozilla.org/docs/Web/API/Fetch_API) `fetch` to allow you to configure the [caching](/docs/app/building-your-application/data-fetching/caching-and-revalidating#fetch-requests) and [revalidating](/docs/app/building-your-application/data-fetching/caching-and-revalidating#revalidating-data) behavior for each fetch request on the server. You can use `fetch` in [Server Components](/docs/app/building-your-application/rendering/server-components), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations). For example: +## Examples -```tsx filename="app/page.tsx" switcher -export default async function Page() { - const data = await fetch('https://api.example.com/...').then((res) => - res.json() - ) +### Fetching data on the server with the `fetch` API - return '...' -} -``` +This component will fetch and display a list of blog posts. The response from `fetch` will be automatically cached. -```jsx filename="app/page.js" switcher +```tsx export default async function Page() { - const data = await fetch('https://api.example.com/...').then((res) => - res.json() + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() + return ( + ) - - return '...' } ``` -By default, `fetch` requests retrieve fresh data. Using it will cause a whole route to be [dynamically rendered](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) and data will not be cached. +If you are not using any [dynamic functions](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in your application, this page will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). -You can cache `fetch` requests by setting the `cache` option to `force-cache`. This means data will be cached, and the component will be [statically rendered](/docs/app/building-your-application/rendering/server-components#static-rendering-default): +If you do _not_ want to cache the response from `fetch`, you can do the following: -```js -fetch('https://...', { cache: 'force-cache' }) +```jsx +let data = await fetch('https://api.vercel.app/blog', { cache: 'force-cache' }) ``` -Alternatively, if using [PPR](/docs/app/building-your-application/rendering/partial-prerendering), we recommend wrapping components that use `fetch` requests in Suspense boundaries. This will ensure only the component that uses `fetch` is dynamically rendered and streamed in, rather than the entire page: - -```tsx filename="@/app/ui/cart.tsx switcher -import { Suspense } from 'react' +### Fetching data on the server with an ORM or database +This component will always fetch and display a dynamic, up-to-date list of blog posts. -export default async function Cart() { - const res = await fetch('https://api.example.com/...') +```tsx +import { db, posts } from '@/lib/db' - return '...' -} - -export default function Navigation() { +export default async function Page() { + let allPosts = await db.select().from(posts) return ( - <> - }> - - - <> + ) } ``` -See the [caching and revalidating](/docs/app/building-your-application/data-fetching/caching-and-revalidating) docs for more information. - -> **Good to know:** In Next.js 14 and earlier, `fetch` requests were cached by default. See the [upgrade guide](/docs/app/building-your-application/upgrading/version-15) for more information. - -### Request Memoization - -If you need to fetch the same data in multiple components in a tree, you do not have to fetch data globally and pass props down. Instead, you can fetch data in the components that need the data without worrying about the performance implications of making multiple requests for the same data. - -This is possible because `fetch` requests with the same URL and options are automatically memoized during a React render pass. +The database call is _not_ cached. This example would opt your Next.js application into [server-side rendering](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering). If you want to cache the response and allow the page to be prerendered, see this example. -Learn more about [request memoization](/docs/app/building-your-application/caching#request-memoization). +### Fetching data on the client -## ORMs and Database Clients +We recommend first attempting to fetch data on the server-side. -You can call your ORM or database client in [Server Components](/docs/app/building-your-application/rendering/server-components), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations). +However, there are still cases where client-side data fetching makes sense. In these scenarios, you can manually call `fetch` in a `useEffect` (not recommended), or lean on popular React libraries in the community (such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/latest)) for client fetching. -You can use [React `cache`](https://react.dev/reference/react/cache) to memoize data requests during a React render pass. For example, although the `getItem` function is called in both layout and page, only one query will be made to the database: - -```ts filename="app/utils.ts" switcher -import { cache } from 'react' +```tsx +'use client' -export const getItem = cache(async (id: string) => { - const item = await db.item.findUnique({ id }) - return item -}) -``` +import { useState, useEffect } from 'react' -```js filename="app/utils.js" switcher -import { cache } from 'react' +export function Posts() { + const [posts, setPosts] = useState(null) -export const getItem = cache(async (id) => { - const item = await db.item.findUnique({ id }) - return item -}) -``` + useEffect(() => { + async function fetchPosts() { + let res = await fetch('https://api.vercel.app/blog') + let data = await res.json() + setPosts(data) + } + fetchPosts() + }, []) -```tsx filename="app/item/[id]/layout.tsx" switcher -import { getItem } from '@/utils/get-item' + if (!posts) return
Loading...
-export default async function Layout({ - params: { id }, -}: { - params: { id: string } -}) { - const item = await getItem(id) - // ... + return ( + + ) } ``` -```jsx filename="app/item/[id]/layout.js" switcher -import { getItem } from '@/utils/get-item' +### Caching data with an ORM or Database -export default async function Layout({ params: { id } }) { - const item = await getItem(id) - // ... -} -``` +You can use the `unstable_cache` API to cache the response to allow pages to be prerendered when running `next build`. -```tsx filename="app/item/[id]/page.tsx" switcher -import { getItem } from '@/utils/get-item' +```tsx +import { unstable_cache } from 'next/cache' +import { db, posts } from '@/lib/db' -export default async function Page({ - params: { id }, -}: { - params: { id: string } -}) { - const item = await getItem(id) - // ... -} -``` +const getPosts = unstable_cache( + async () => { + return await db.select().from(posts) + }, + ['posts'], // part of the cache key + // optionally use ISR or add cache tags + { revalidate: 3600, tags: ['posts'] } +) -```jsx filename="app/item/[id]/page.js" switcher -import { getItem } from '@/utils/get-item' +export default async function Page() { + const allPosts = await getPosts() -export default async function Page({ params: { id } }) { - const item = await getItem(id) - // ... + return ( + + ) } ``` -You can also configure the caching and revalidating behavior of these requests using the experimental [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) and [`unstable_noStore`](/docs/app/api-reference/functions/unstable_noStore) APIs. - -## Data Fetching Libraries +This example caches the result of the database query for 1 hour (3600 seconds). It also adds the cache tag `posts` which can then be invalidated with [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). -You can fetch data in Client Components using data fetching libraries such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/latest). These libraries provide their own APIs for caching, revalidating, and mutating data. +### Reusing data across multiple functions -For example, using SWR to fetch data periodically on the client: +Next.js uses APIs like `generateMetadata` and `generateStaticParams` where you will need to use the same data fetched in the `page`. -```tsx filename="app/page.tsx switcher -"use client" +If you are using `fetch`, requests are automatically [memoized](https://nextjs.org/docs/app/building-your-application/caching#request-memoization). This means you can safely call the same URL with the same options, and only one request will be made. -import useSWR from 'swr' -import fetcher from '@/utils/fetcher' +```jsx +import { notFound } from 'next/navigation' -export default function PollingComponent { - // Polling interval set to 2000 milliseconds - const { data } = useSWR('/api/data', fetcher, { refreshInterval: 2000 }); - - return '...' +interface Post { + id: string + title: string + content: string } -``` - -```js filename="app/page.tsx" switcher -"use client" -import useSWR from 'swr' -import fetcher from '@/utils/fetcher' - -export default function PollingComponent { - // Polling interval set to 2000 milliseconds - const { data } = useSWR('/api/data', fetcher, { refreshInterval: 2000 }); - - return '...' +async function getPost(id: string) { + let res = await fetch(`https://api.example.com/posts/${id}`) + let post: Post = await res.json() + if (!post) notFound() + return post } -``` - -## Route Handlers - -If you need to create API endpoints, Next.js supports [Route Handlers](/docs/app/building-your-application/routing/route-handlers). Route Handlers execute on the server and prevent sensitive information from being exposed to the client (e.g. API credentials). - -For example, using SWR to call a Route Handler: -```tsx filename="app/ui/message.tsx" switcher -'use client' - -import useSWR from 'swr' -import fetcher from '@/utils/fetcher' +export async function generateStaticParams() { + const posts = await fetch('https://api.example.com/posts').then(res => res.json()) -export default function Message() { - const { data } = useSWR('/api/messages', fetcher) - - return '...' + return posts.map((post: Post) => ({ + id: post.id, + })) } -``` -```jsx filename="app/ui/message.js" switcher -'use client' +export async function generateMetadata({ params }: { params: { id: string } }) { + let post = await getPost(params.id) -import useSWR from 'swr' -import fetcher from '@/utils/fetcher' + return { + title: post.title + } +} -export default function Message() { - const { data } = useSWR('/api/messages', fetcher) +export default async function Page({ params }: { params: { id: string } }) { + let post = await getPost(params.id) - return '...' + return ( +
+

{post.title}

+

{post.content}

+
+ ) } ``` -```tsx filename="app/api/messages/route.ts" switcher -export async function GET() { - const data = await fetch('https://...', { - headers: { - 'Content-Type': 'application/json', - 'API-Key': process.env.DATA_API_KEY, - }, - }).then((res) => res.json()) +If you are _not_ using `fetch`, and instead using an ORM or database directly, you can wrap your data fetch with the React `cache` function. This will de-duplicate and only make one query. - return Response.json({ data }) -} -``` +```jsx +import { cache } from 'react' +import { db, posts, eq } from '@/lib/db' // Example with Drizzle ORM +import { notFound } from 'next/navigation' -```js filename="app/api/messages/route.js" switcher -export async function GET() { - const data = await fetch('https://...', { - headers: { - 'Content-Type': 'application/json', - 'API-Key': process.env.DATA_API_KEY, - }, - }).then((res) => res.json()) +export const getPost = cache(async (id: string) => { + const post = await db.query.posts.findFirst({ + where: eq(posts.id, parseInt(id)) + }) - return Response.json({ data }) -} + if (!post) notFound() + return post +}) ``` -See the [Route Handler](/docs/app/building-your-application/routing/route-handlers) docs for more examples. +### Revalidating cached data -> **Good to know**: Since Server Components render on the server, you don't need to call a Route Handler from a Server Component. You can access your data directly. +Learn more about revalidating cached data with [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). ## Patterns diff --git a/docs/02-app/01-building-your-application/02-data-fetching/02-caching-and-revalidating.mdx b/docs/02-app/01-building-your-application/02-data-fetching/02-caching-and-revalidating.mdx deleted file mode 100644 index 955ca4a5cec11..0000000000000 --- a/docs/02-app/01-building-your-application/02-data-fetching/02-caching-and-revalidating.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Caching and Revalidating -nav_title: Caching and Revalidating -description: Learn how to cache and revalidate data in your Next.js application. ---- - -## Caching - -Caching is the process of storing data to reduce the number of requests made to the server. Next.js provides a built-in [Data Cache](/docs/app/building-your-application/caching) for individual data requests, giving you granular control of caching behavior. - -### `fetch` requests - -`fetch` requests are not cached by default in Next.js 15. - -To cache an individual `fetch` request, you can use the `cache: 'force-cache'` option: - -```js -fetch('https://...', { cache: 'force-cache' }) -``` - -### Data fetching libraries and ORMs - -To cache specific requests to your database or ORM, you can use the [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) API: - -```jsx -import { getUser } from './data' -import { unstable_cache } from 'next/cache' - -const getCachedUser = unstable_cache(async (id) => getUser(id), ['my-app-user']) - -export default async function Component({ userID }) { - const user = await getCachedUser(userID) - return user -} -``` - -## Revalidating data - -Revalidation is the process of purging the Data Cache and re-fetching the latest data. This is useful when your data changes and you want to ensure you show the latest information while still benefiting from the speed of static rendering. - -Cached data can be revalidated in two ways: - -- **Time-based revalidation**: Automatically revalidate data after a certain amount of time has passed. This is useful for data that changes infrequently and freshness is not as critical. -- **On-demand revalidation**: Manually revalidate data based on an event (e.g. form submission). On-demand revalidation can use a tag-based or path-based approach to revalidate groups of data at once. This is useful when you want to ensure the latest data is shown as soon as possible (e.g. when content from your headless CMS is updated). - -### Time-based revalidation - -To revalidate data at a timed interval, you can use the `next.revalidate` option of `fetch` to set the cache lifetime of a resource (in seconds). - -```js -fetch('https://...', { next: { revalidate: 3600 } }) // revalidate at most every hour -``` - -Alternatively, to revalidate all requests in a route segment, you can use the [Segment Config Options](/docs/app/api-reference/file-conventions/route-segment-config). - -```jsx filename="layout.js | page.js" -export const revalidate = 3600 // revalidate at most every hour -``` - -Learn [how time-based revalidation works](/docs/app/building-your-application/caching#revalidating-1) - -> **Good to know:** -> -> - If you have multiple fetch requests in a statically rendered route, and each has a different revalidation frequency. The lowest time will be used for all requests. -> - For dynamically rendered routes, each `fetch` request will be revalidated independently. -> - To save server resources, we recommend setting a high revalidation time whenever possible. For instance, 1 hour instead of 1 second. If you need real-time data, consider switching to [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) or client-side data fetching. - -### On-demand revalidation - -Data can be revalidated on-demand with the [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) and [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) APIs. - -Use `revalidatePath` in [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers) to revalidate data for specific routes: - -```tsx filename="@/app/actions.tsx switcher -import { revalidatePath } from 'next/cache' - -export async function createPost() { - // Mutate data - revalidatePath('/posts') -} -``` - -```jsx filename="@/app/actions.js switcher -import { revalidatePath } from 'next/cache' - -export async function createPost() { - // Mutate data - revalidatePath('/posts') -} -``` - -Use `revalidateTag` to revalidate `fetch` requests across routes. - -1. When using `fetch`, you have the option to tag cache entries with one or more tags. -2. Then, you can call `revalidateTag` to revalidate all entries associated with that tag. - -For example, the following `fetch` request adds the cache tag `collection`: - -```tsx filename="app/page.tsx" switcher -export default async function Page() { - const res = await fetch('https://...', { next: { tags: ['collection'] } }) - const data = await res.json() - // ... -} -``` - -```jsx filename="app/page.js" switcher -export default async function Page() { - const res = await fetch('https://...', { next: { tags: ['collection'] } }) - const data = await res.json() - // ... -} -``` - -You can then revalidate this `fetch` call tagged with `collection` by calling `revalidateTag`: - -```ts filename="@/app/actions.ts" switcher -'use server' - -import { revalidateTag } from 'next/cache' - -export async function action() { - revalidateTag('collection') -} -``` - -```js filename="@/app/actions.js" switcher -'use server' - -import { revalidateTag } from 'next/cache' - -export async function action() { - revalidateTag('collection') -} -``` - -Learn [how on-demand revalidation works](/docs/app/building-your-application/caching#on-demand-revalidation). - -### Error handling and revalidation - -If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data. diff --git a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx new file mode 100644 index 0000000000000..11ec48c01f062 --- /dev/null +++ b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx @@ -0,0 +1,472 @@ +--- +title: Incremental Static Regeneration (ISR) +description: Learn how to create or update static pages at runtime with Incremental Static Regeneration. +--- + +
+ Examples + +- [Next.js Commerce](https://vercel.com/templates/next.js/nextjs-commerce) +- [On-Demand ISR](https://on-demand-isr.vercel.app) +- [Next.js Forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) + +
+ +Incremental Static Regeneration (ISR) enables you to: + +- Update static content without rebuilding the entire site +- Reduce server load by serving prerendered, static pages for most requests +- Ensure proper `cache-control` headers are automatically added to pages +- Handle large amounts of content pages without long `next build` times + +Here's a minimal example: + + + +```tsx filename="app/blog/[id]/page.tsx" switcher +interface Post { + id: string + title: string + content: string +} + +// Next.js will invalidate the cache when a +// request comes in, at most once every 60 seconds. +export const revalidate = 60 + +// We'll prerender only the params from `generateStaticParams` at build time. +// If a request comes in for a path that hasn't been generated, +// Next.js will server-render the page on-demand. +export const dynamicParams = true // or false, to 404 on unknown paths + +export async function generateStaticParams() { + let posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) => + res.json() + ) + return posts.map((post) => ({ + id: post.id, + })) +} + +export default async function Page({ params }: { params: { id: string } }) { + let post = await fetch(`https://api.vercel.app/blog/${params.id}`).then( + (res) => res.json() + ) + return ( +
+

{post.title}

+

{post.content}

+
+ ) +} +``` + +```jsx filename="app/blog/[id]/page.jsx" switcher +// Next.js will invalidate the cache when a +// request comes in, at most once every 60 seconds. +export const revalidate = 60; + +// We'll prerender only the params from `generateStaticParams` at build time. +// If a request comes in for a path that hasn't been generated, +// Next.js will server-render the page on-demand. +export const dynamicParams = true // or false, to 404 on unknown paths + +export async function generateStaticParams() { + let posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) => res.json()); + return posts.map((post) => ({ + id: post.id, + })); +} + +export default async function Page({ params }: { params: { id: string } }) { + let post = await fetch(`https://api.vercel.app/blog/${params.id}`).then((res) => res.json()); + return ( +
+

{post.title}

+

{post.content}

+
+ ); +} +``` + +
+ + + +```tsx filename="pages/blog/[id].tsx" switcher +import { GetStaticPaths, GetStaticProps } from 'next' + +interface Post { + id: string + title: string + content: string +} + +interface Props { + post: Post +} + +export const getStaticPaths: GetStaticPaths = async () => { + let posts = await fetch('https://api.vercel.app/blog').then((res) => + res.json() + ) + let paths = posts.map((post: Post) => ({ + params: { id: post.id }, + })) + + // We'll prerender only these paths at build time. + // { fallback: 'blocking' } will server-render pages + // on-demand if the path doesn't exist. + return { paths, fallback: false } +} + +export const getStaticProps: GetStaticProps = async ({ params }) => { + let post = await fetch(`https://api.vercel.app/blog/${params.id}`).then( + (res) => res.json() + ) + + return { + props: { post }, + // Next.js will invalidate the cache when a + // request comes in, at most once every 60 seconds. + revalidate: 60, + } +} + +export default function Page({ post }: Props) { + return ( +
+

{post.title}

+

{post.content}

+
+ ) +} +``` + +```jsx filename="pages/blog/[id].jsx" switcher +export async function getStaticPaths() { + let posts = await fetch('https://api.vercel.app/blog').then((res) => + res.json() + ) + let paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // We'll prerender only these paths at build time. + // { fallback: false } means other routes should 404. + return { paths, fallback: false } +} + +export async function getStaticProps({ params }) { + let post = await fetch(`https://api.vercel.app/blog/${params.id}`).then( + (res) => res.json() + ) + + return { + props: { post }, + // Next.js will invalidate the cache when a + // request comes in, at most once every 60 seconds. + revalidate: 60, + } +} + +export default function Page({ post }) { + return ( +
+

{post.title}

+

{post.content}

+
+ ) +} +``` + +
+ +Here's how this example works: + +1. During `next build`, all known blog posts are generated (there are 25 in this example) +2. All requests made to these pages (e.g. `/blog/1`) are cached and instantaneous +3. After 60 seconds has passed, the next request will still show the cached (stale) page +4. The cache is invalidated and a new version of the page begins generating in the background +5. Once generated successfully, Next.js will display and cache the updated page +6. If `/blog/26` is requested, Next.js will generated and cached this page on-demand + +## Reference + + + +### Route segment config + +- [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) +- [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams) + +### Functions + +- [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) +- [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) + + + + + +### Functions + +- [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props) +- [`res.revalidate`](/docs/pages/building-your-application/routing/api-routes#response-helpers) + + + +## Examples + + + +### Time-based revalidation + +This fetches and displays a list of blog posts on `/blog`. After an hour, the cache for this page is invalidated on the next visit to the page. Then, in the background, a new version of the page is generated with the latest blog posts. + +```jsx +export const revalidate = 3600 // invalidate every hour + +export default async function Page() { + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() + return ( +
+

Blog Posts

+
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+
+ ) +} +``` + +We recommend setting a high revalidation time. For instance, 1 hour instead of 1 second. If you need more precision, consider using on-demand revalidation. If you need real-time data, consider switching to [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). + +### On-demand revalidation with `revalidatePath` + +For a more precise method of revalidation, invalidate pages on-demand with the `revalidatePath` function. + +For example, this Server Action would get called after adding a new post. Regardless of how you retrieve your data in your Server Component, either using `fetch` or connecting to a database, this will clear the cache for the entire route and allow the Server Component to fetch fresh data. + +```jsx +'use server' + +import { revalidatePath } from 'next/cache' + +export async function createPost() { + // Invalidate the /posts route in the cache + revalidatePath('/posts') +} +``` + +[View a demo](https://on-demand-isr.vercel.app) and [explore the source code](https://github.com/vercel/on-demand-isr). + +### On-demand revalidation with `revalidateTag` + +For most use cases, prefer revalidating entire paths. If you need more granular control, you can use the `revalidateTag` function. For example, you can tag individual `fetch` calls: + +```jsx +export default async function Page() { + let data = await fetch('https://api.vercel.app/blog', { + next: { tags: ['posts'] }, + }) + let posts = await data.json() + // ... +} +``` + +If you are using an ORM or connecting to a database, you can use `unstable_cache`: + +```jsx +import { unstable_cache } from 'next/cache' + +const getCachedPosts = unstable_cache( + async () => { + let posts = await sql`SELECT * FROM posts WHERE published = true` + return posts + }, + ['posts'], + { revalidate: 3600, tags: ['posts'] } +) + +export default async function Page() { + let posts = getCachedPosts() + // ... +} +``` + +You can then use `revalidateTag` in a [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers): + +```jsx +'use server' + +import { revalidateTag } from 'next/cache' + +export async function createPost() { + // Invalidate all data tagged with 'posts' in the cache + revalidateTag('posts') +} +``` + +
+ + + +### On-demand validation with `res.revalidate()` + +For a more precise method of revalidation, use `res.revalidate` to generate a new page on-demand from an API Router. + +For example, this API Route can be called at `/api/revalidate?secret=` to revalidate a given blog post. Create a secret token only known by your Next.js app. This secret will be used to prevent unauthorized access to the revalidation API Route. + +```ts filename="pages/api/revalidate.ts" switcher +import { NextApiRequest, NextApiResponse } from 'next' + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + // Check for secret to confirm this is a valid request + if (req.query.secret !== process.env.MY_SECRET_TOKEN) { + return res.status(401).json({ message: 'Invalid token' }) + } + + try { + // This should be the actual path not a rewritten path + // e.g. for "/posts/[id]" this should be "/posts/1" + await res.revalidate('/posts/1') + return res.json({ revalidated: true }) + } catch (err) { + // If there was an error, Next.js will continue + // to show the last successfully generated page + return res.status(500).send('Error revalidating') + } +} +``` + +If you are using on-demand revalidation, you do not need to specify a `revalidate` time inside of `getStaticProps`. Next.js will use the default value of `false` (no revalidation) and only revalidate the page on-demand when `res.revalidate()` is called. + + + +### Handling uncaught exceptions + + + +If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data. [Learn more about error handling](/docs/app/building-your-application/routing/error-handling). + + + + + +If there is an error inside `getStaticProps` when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling `getStaticProps`. + +```jsx filename="pages/blog/[id].jsx" switcher +export async function getStaticProps({ params }) { + // If this request throws an uncaught error, Next.js will + // not invalidate the currently shown page and + // retry getStaticProps on the next request. + let res = await fetch(`https://api.vercel.app/blog/${params.id}`) + let posts = await res.json() + + if (!res.ok) { + // If there is a server error, you might want to + // throw an error instead of returning so that the cache is not updated + // until the next successful request. + throw new Error(`Failed to fetch posts, received status ${res.status}`) + } + + return { + props: { post }, + // Next.js will attempt to regenerate the page: + // - When a request comes in + // - At most once every 60 seconds + revalidate: 60, // In seconds + } +} +``` + + + +### Customizing the cache location + +Caching and revalidating pages (with Incremental Static Regeneration) use the same shared cache. When [deploying to Vercel](https://vercel.com/docs/incremental-static-regeneration?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), the ISR cache is automatically persisted to durable storage. + +When self-hosting, the ISR cache is stored to the filesystem (on disk) on your Next.js server. This works automatically when self-hosting using both the Pages and App Router. + +You can configure the Next.js cache location if you want to persist cached pages and data to durable storage, or share the cache across multiple containers or instances of your Next.js application. [Learn more](/docs/app/building-your-application/deploying#caching-and-isr). + +## Error handling and revalidation + +If there is an error inside `getStaticProps` when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling `getStaticProps`. + +```jsx +export async function getStaticProps() { + // If this request throws an uncaught error, Next.js will + // not invalidate the currently shown page and + // retry getStaticProps on the next request. + let res = await fetch('https://.../posts') + let posts = await res.json() + + if (!res.ok) { + // If there is a server error, you might want to + // throw an error instead of returning so that the cache is not updated + // until the next successful request. + throw new Error(`Failed to fetch posts, received status ${res.status}`) + } + + // If the request was successful, return the posts + // and revalidate every 10 seconds. + return { + props: { + posts, + }, + revalidate: 10, + } +} +``` + +## Troubleshooting + +### Debugging cached data in local development + +If you are using the `fetch` API, you can add additional logging to understand which requests are cached or uncached. Learn more about the `logging` option. + +```jsx filename="next.config.js" +module.exports = { + logging: { + fetches: { + fullUrl: true, + }, + }, +} +``` + +### Verifying correct production behavior + +To verify your pages are cached and revalidated correctly in production, you can test locally by running `next build` and then `next start` to run the production Next.js server. + +This will allow you to test ISR behavior as it would work in a production environment. For further debugging, add the following environment variable to your `.env` file: + +```bash filename=".env" +NEXT_PRIVATE_DEBUG_CACHE=1 +``` + +This will make the Next.js server console log ISR cache hits and misses. You can inspect the output to see which pages are generated during `next build`, as well as how pages are updated as paths are accessed on-demand. + +## Caveats + +- ISR is only supported when using the Node.js runtime (default). +- If you have multiple `fetch` requests in a statically rendered route, and each has a different `revalidate` frequency, the lowest time will be used for all requests. +- Middleware won't be executed for on-demand ISR requests, meaning any path rewrites or logic in Middleware will not be applied. Ensure you are revalidating the exact path. For example, `/post/1` instead of a rewritten `/post-1`. + +## Version history + +| Version | Changes | +| --------- | ----------------------------------------------------------------------------------- | +| `v14.1.0` | Custom `cacheHandler` is stable. | +| `v13.0.0` | App Router is introduced. | +| `v12.2.0` | Pages Router: On-Demand ISR is stable | +| `v12.0.0` | Pages Router: [Bot-aware ISR fallback](/blog/next-12#bot-aware-isr-fallback) added. | +| `v9.5.0` | Pages Router: [Stable ISR introduced](/blog/next-9-5). | diff --git a/docs/02-app/01-building-your-application/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index e9f0d25565ab8..25a54dc53f270 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -122,7 +122,7 @@ export default function Component() { Next.js can cache responses, generated static pages, build outputs, and other static assets like images, fonts, and scripts. -Caching and revalidating pages (using Incremental Static Regeneration (ISR) or newer functions in the App Router) use the **same shared cache**. By default, this cache is stored to the filesystem (on disk) on your Next.js server. **This works automatically when self-hosting** using both the Pages and App Router. +Caching and revalidating pages (with [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration)) use the **same shared cache**. By default, this cache is stored to the filesystem (on disk) on your Next.js server. **This works automatically when self-hosting** using both the Pages and App Router. You can configure the Next.js cache location if you want to persist cached pages and data to durable storage, or share the cache across multiple containers or instances of your Next.js application. diff --git a/docs/03-pages/01-building-your-application/03-data-fetching/04-incremental-static-regeneration.mdx b/docs/03-pages/01-building-your-application/03-data-fetching/04-incremental-static-regeneration.mdx index 40f5b5cfa2497..12a53d4984270 100644 --- a/docs/03-pages/01-building-your-application/03-data-fetching/04-incremental-static-regeneration.mdx +++ b/docs/03-pages/01-building-your-application/03-data-fetching/04-incremental-static-regeneration.mdx @@ -1,182 +1,7 @@ --- title: Incremental Static Regeneration (ISR) description: Learn how to create or update static pages at runtime with Incremental Static Regeneration. +source: app/building-your-application/data-fetching/incremental-static-regeneration --- -
- Examples - -- [Next.js Commerce](https://nextjs.org/commerce) -- [GitHub Reactions Demo](https://reactions-demo.vercel.app/) -- [Static Tweet Demo](https://static-tweet.vercel.app/) - -
- -Next.js allows you to create or update static pages _after_ you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, **without needing to rebuild the entire site**. With ISR, you can retain the benefits of static while scaling to millions of pages. - -> **Good to know**: The [`edge` runtime](/docs/pages/api-reference/edge) is currently not compatible with ISR, although you can leverage `stale-while-revalidate` by setting the `cache-control` header manually. - -To use ISR, add the `revalidate` prop to `getStaticProps`: - -```jsx -function Blog({ posts }) { - return ( - - ) -} - -// This function gets called at build time on server-side. -// It may be called again, on a serverless function, if -// revalidation is enabled and a new request comes in -export async function getStaticProps() { - const res = await fetch('https://.../posts') - const posts = await res.json() - - return { - props: { - posts, - }, - // Next.js will attempt to re-generate the page: - // - When a request comes in - // - At most once every 10 seconds - revalidate: 10, // In seconds - } -} - -// This function gets called at build time on server-side. -// It may be called again, on a serverless function, if -// the path has not been generated. -export async function getStaticPaths() { - const res = await fetch('https://.../posts') - const posts = await res.json() - - // Get the paths we want to pre-render based on posts - const paths = posts.map((post) => ({ - params: { id: post.id }, - })) - - // We'll pre-render only these paths at build time. - // { fallback: 'blocking' } will server-render pages - // on-demand if the path doesn't exist. - return { paths, fallback: 'blocking' } -} - -export default Blog -``` - -When a request is made to a page that was pre-rendered at build time, it will initially show the cached page. - -- Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous. -- After the 10-second window, the next request will still show the cached (stale) page -- Next.js triggers a regeneration of the page in the background. -- Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered. - -When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. ISR on Vercel [persists the cache globally and handles rollbacks](https://vercel.com/docs/concepts/next.js/incremental-static-regeneration?utm_source=next-site&utm_medium=docs&utm_campaign=next-website). - -> **Good to know**: Check if your upstream data provider has caching enabled by default. You might need to disable (e.g. `useCdn: false`), otherwise a revalidation won't be able to pull fresh data to update the ISR cache. Caching can occur at a CDN (for an endpoint being requested) when it returns the `Cache-Control` header. - -## On-Demand Revalidation - -If you set a `revalidate` time of `60`, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is from someone visiting that page after the minute has passed. - -Starting with `v12.2.0`, Next.js supports On-Demand Incremental Static Regeneration to manually purge the Next.js cache for a specific page. This makes it easier to update your site when: - -- Content from your headless CMS is created or updated -- Ecommerce metadata changes (price, description, category, reviews, etc.) - -Inside `getStaticProps`, you do not need to specify `revalidate` to use on-demand revalidation. If `revalidate` is omitted, Next.js will use the default value of `false` (no revalidation) and only revalidate the page on-demand when `revalidate()` is called. - -> **Good to know**: [Middleware](/docs/pages/building-your-application/routing/middleware) won't be executed for On-Demand ISR requests. Instead, call `revalidate()` on the _exact_ path that you want revalidated. For example, if you have `pages/blog/[slug].js` and a rewrite from `/post-1` -> `/blog/post-1`, you would need to call `res.revalidate('/blog/post-1')`. - -### Using On-Demand Revalidation - -First, create a secret token only known by your Next.js app. This secret will be used to prevent unauthorized access to the revalidation API Route. You can access the route (either manually or with a webhook) with the following URL structure: - -```bash filename="Terminal" -https:///api/revalidate?secret= -``` - -Next, add the secret as an [Environment Variable](/docs/pages/building-your-application/configuring/environment-variables) to your application. Finally, create the revalidation API Route: - -```js filename="pages/api/revalidate.js" -export default async function handler(req, res) { - // Check for secret to confirm this is a valid request - if (req.query.secret !== process.env.MY_SECRET_TOKEN) { - return res.status(401).json({ message: 'Invalid token' }) - } - - try { - // this should be the actual path not a rewritten path - // e.g. for "/blog/[slug]" this should be "/blog/post-1" - await res.revalidate('/path-to-revalidate') - return res.json({ revalidated: true }) - } catch (err) { - // If there was an error, Next.js will continue - // to show the last successfully generated page - return res.status(500).send('Error revalidating') - } -} -``` - -[View our demo](https://on-demand-isr.vercel.app) to see on-demand revalidation in action and provide feedback. - -### Testing on-Demand ISR during development - -When running locally with `next dev`, `getStaticProps` is invoked on every request. To verify your on-demand ISR configuration is correct, you will need to create a [production build](/docs/pages/api-reference/cli/next#next-build-options) and start the [production server](/docs/pages/api-reference/cli/next#next-start-options): - -```bash filename="Terminal" -$ next build -$ next start -``` - -Then, you can confirm that static pages have successfully revalidated. - -## Error handling and revalidation - -If there is an error inside `getStaticProps` when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling `getStaticProps`. - -```jsx -export async function getStaticProps() { - // If this request throws an uncaught error, Next.js will - // not invalidate the currently shown page and - // retry getStaticProps on the next request. - const res = await fetch('https://.../posts') - const posts = await res.json() - - if (!res.ok) { - // If there is a server error, you might want to - // throw an error instead of returning so that the cache is not updated - // until the next successful request. - throw new Error(`Failed to fetch posts, received status ${res.status}`) - } - - // If the request was successful, return the posts - // and revalidate every 10 seconds. - return { - props: { - posts, - }, - revalidate: 10, - } -} -``` - -## Self-hosting ISR - -Incremental Static Regeneration (ISR) works on [self-hosted Next.js sites](/docs/pages/building-your-application/deploying#self-hosting) out of the box when you use `next start`. - -Learn more about [self-hosting Next.js](/docs/pages/building-your-application/deploying#self-hosting). - -## Version History - -| Version | Changes | -| --------- | --------------------------------------------------------------------------------------- | -| `v14.1.0` | Custom `cacheHandler` is stable. | -| `v12.2.0` | On-Demand ISR is stable | -| `v12.1.0` | On-Demand ISR added (beta). | -| `v12.0.0` | [Bot-aware ISR fallback](https://nextjs.org/blog/next-12#bot-aware-isr-fallback) added. | -| `v9.5.0` | Base Path added. | +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} From e98c85743933fe9bfcb44639f8e1f4e9557728d5 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 18:43:00 -0500 Subject: [PATCH 2/6] Code blocks --- .../02-data-fetching/01-fetching.mdx | 180 ++++++++++++++++-- 1 file changed, 159 insertions(+), 21 deletions(-) diff --git a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx index 3ead2b9ba4bdf..66e027269eb0d 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx @@ -17,7 +17,21 @@ This guide will walk you through the basics of data fetching and caching in Next Here's a minimal example of data fetching in Next.js: -```tsx +```tsx filename="app/page.tsx" switcher +export default async function Page() { + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +```jsx filename="app/page.js" switcher export default async function Page() { let data = await fetch('https://api.vercel.app/blog') let posts = await data.json() @@ -35,9 +49,9 @@ This example demonstrates a basic server-side data fetch using the `fetch` API i ## Reference -- [`fetch`](https://nextjs.org/docs/app/api-reference/functions/fetch) +- [`fetch`](/docs/app/api-reference/functions/fetch) - React [`cache`](https://react.dev/reference/react/cache) -- Next.js [`unstable_cache`](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) +- Next.js [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) ## Examples @@ -45,7 +59,7 @@ This example demonstrates a basic server-side data fetch using the `fetch` API i This component will fetch and display a list of blog posts. The response from `fetch` will be automatically cached. -```tsx +```tsx filename="app/page.tsx" switcher export default async function Page() { let data = await fetch('https://api.vercel.app/blog') let posts = await data.json() @@ -59,11 +73,25 @@ export default async function Page() { } ``` -If you are not using any [dynamic functions](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in your application, this page will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). +```jsx filename="app/page.js" switcher +export default async function Page() { + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +If you are not using any [dynamic functions](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in your application, this page will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). If you do _not_ want to cache the response from `fetch`, you can do the following: -```jsx +```js let data = await fetch('https://api.vercel.app/blog', { cache: 'force-cache' }) ``` @@ -71,7 +99,7 @@ let data = await fetch('https://api.vercel.app/blog', { cache: 'force-cache' }) This component will always fetch and display a dynamic, up-to-date list of blog posts. -```tsx +```tsx filename="app/page.tsx" switcher import { db, posts } from '@/lib/db' export default async function Page() { @@ -86,15 +114,59 @@ export default async function Page() { } ``` -The database call is _not_ cached. This example would opt your Next.js application into [server-side rendering](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering). If you want to cache the response and allow the page to be prerendered, see this example. +```jsx filename="app/page.js" switcher +import { db, posts } from '@/lib/db' + +export default async function Page() { + let allPosts = await db.select().from(posts) + return ( +
    + {allPosts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +The database call is _not_ cached. This example would opt your Next.js application into [server-side rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). If you want to cache the response and allow the page to be prerendered, see this example. ### Fetching data on the client We recommend first attempting to fetch data on the server-side. -However, there are still cases where client-side data fetching makes sense. In these scenarios, you can manually call `fetch` in a `useEffect` (not recommended), or lean on popular React libraries in the community (such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/latest)) for client fetching. +However, there are still cases where client-side data fetching makes sense. In these scenarios, you can manually call `fetch` in a `useEffect` (not recommended), or lean on popular React libraries in the community (such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/latest)) for client fetching. -```tsx +```tsx filename="app/page.tsx" switcher +'use client' + +import { useState, useEffect } from 'react' + +export function Posts() { + const [posts, setPosts] = useState(null) + + useEffect(() => { + async function fetchPosts() { + let res = await fetch('https://api.vercel.app/blog') + let data = await res.json() + setPosts(data) + } + fetchPosts() + }, []) + + if (!posts) return
Loading...
+ + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +```jsx filename="app/page.js" switcher 'use client' import { useState, useEffect } from 'react' @@ -127,7 +199,32 @@ export function Posts() { You can use the `unstable_cache` API to cache the response to allow pages to be prerendered when running `next build`. -```tsx +```tsx filename="app/page.tsx" switcher +import { unstable_cache } from 'next/cache' +import { db, posts } from '@/lib/db' + +const getPosts = unstable_cache( + async () => { + return await db.select().from(posts) + }, + ['posts'], + { revalidate: 3600, tags: ['posts'] } +) + +export default async function Page() { + const allPosts = await getPosts() + + return ( +
    + {allPosts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +```jsx filename="app/page.js" switcher import { unstable_cache } from 'next/cache' import { db, posts } from '@/lib/db' @@ -135,8 +232,7 @@ const getPosts = unstable_cache( async () => { return await db.select().from(posts) }, - ['posts'], // part of the cache key - // optionally use ISR or add cache tags + ['posts'], { revalidate: 3600, tags: ['posts'] } ) @@ -153,15 +249,15 @@ export default async function Page() { } ``` -This example caches the result of the database query for 1 hour (3600 seconds). It also adds the cache tag `posts` which can then be invalidated with [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). +This example caches the result of the database query for 1 hour (3600 seconds). It also adds the cache tag `posts` which can then be invalidated with [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). ### Reusing data across multiple functions Next.js uses APIs like `generateMetadata` and `generateStaticParams` where you will need to use the same data fetched in the `page`. -If you are using `fetch`, requests are automatically [memoized](https://nextjs.org/docs/app/building-your-application/caching#request-memoization). This means you can safely call the same URL with the same options, and only one request will be made. +If you are using `fetch`, requests are automatically [memoized](/docs/app/building-your-application/caching#request-memoization). This means you can safely call the same URL with the same options, and only one request will be made. -```jsx +```tsx filename="app/page.tsx" switcher import { notFound } from 'next/navigation' interface Post { @@ -178,7 +274,9 @@ async function getPost(id: string) { } export async function generateStaticParams() { - const posts = await fetch('https://api.example.com/posts').then(res => res.json()) + let posts = await fetch('https://api.example.com/posts').then((res) => + res.json() + ) return posts.map((post: Post) => ({ id: post.id, @@ -189,7 +287,7 @@ export async function generateMetadata({ params }: { params: { id: string } }) { let post = await getPost(params.id) return { - title: post.title + title: post.title, } } @@ -205,6 +303,46 @@ export default async function Page({ params }: { params: { id: string } }) { } ``` +```jsx filename="app/page.js" switcher +import { notFound } from 'next/navigation' + +async function getPost(id) { + let res = await fetch(`https://api.example.com/posts/${id}`) + let post = await res.json() + if (!post) notFound() + return post +} + +export async function generateStaticParams() { + let posts = await fetch('https://api.example.com/posts').then((res) => + res.json() + ) + + return posts.map((post) => ({ + id: post.id, + })) +} + +export async function generateMetadata({ params }) { + let post = await getPost(params.id) + + return { + title: post.title, + } +} + +export default async function Page({ params }) { + let post = await getPost(params.id) + + return ( +
+

{post.title}

+

{post.content}

+
+ ) +} +``` + If you are _not_ using `fetch`, and instead using an ORM or database directly, you can wrap your data fetch with the React `cache` function. This will de-duplicate and only make one query. ```jsx @@ -212,9 +350,9 @@ import { cache } from 'react' import { db, posts, eq } from '@/lib/db' // Example with Drizzle ORM import { notFound } from 'next/navigation' -export const getPost = cache(async (id: string) => { +export const getPost = cache(async (id) => { const post = await db.query.posts.findFirst({ - where: eq(posts.id, parseInt(id)) + where: eq(posts.id, parseInt(id)), }) if (!post) notFound() @@ -224,7 +362,7 @@ export const getPost = cache(async (id: string) => { ### Revalidating cached data -Learn more about revalidating cached data with [Incremental Static Regeneration](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration). +Learn more about revalidating cached data with [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). ## Patterns From 152d3a9a15021462a5ab2ffa5747581bd7544417 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 18:53:21 -0500 Subject: [PATCH 3/6] Update links --- .../01-routing/13-route-handlers.mdx | 28 +++++++------------ .../04-incremental-static-regeneration.mdx | 1 + .../03-rendering/01-server-components.mdx | 6 ++-- .../03-rendering/03-composition-patterns.mdx | 6 ++-- .../07-configuring/01-typescript.mdx | 4 +-- .../10-deploying/02-static-exports.mdx | 2 +- .../11-upgrading/04-app-router-migration.mdx | 8 +++--- .../02-api-reference/04-functions/headers.mdx | 2 +- .../incrementalCacheHandlerPath.mdx | 8 ++++-- docs/02-app/index.mdx | 3 +- 10 files changed, 33 insertions(+), 35 deletions(-) diff --git a/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx b/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx index eef98d84a3460..e4c18763b484a 100644 --- a/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx +++ b/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx @@ -115,36 +115,28 @@ The following examples show how to combine Route Handlers with other Next.js API ### Revalidating Cached Data -You can [revalidate cached data](/docs/app/building-your-application/data-fetching/caching-and-revalidating#revalidating-data) using the [`next.revalidate`](/docs/app/building-your-application/data-fetching/caching-and-revalidating#revalidating-data) option: +You can [revalidate cached data](/docs/app/building-your-application/data-fetching/incremental-static-regeneration) using Incremental Static Regeneration (ISR): + +```ts filename="app/posts/route.ts" switcher +export const revalidate = 60 -```ts filename="app/items/route.ts" switcher export async function GET() { - const res = await fetch('https://data.mongodb-api.com/...', { - next: { revalidate: 60 }, // Revalidate every 60 seconds - }) - const data = await res.json() + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() - return Response.json(data) + return Response.json(posts) } ``` -```js filename="app/items/route.js" switcher +```js filename="app/posts/route.js" switcher export async function GET() { - const res = await fetch('https://data.mongodb-api.com/...', { - next: { revalidate: 60 }, // Revalidate every 60 seconds - }) - const data = await res.json() + let data = await fetch('https://api.vercel.app/blog') + let posts = await data.json() return Response.json(data) } ``` -Alternatively, you can use the [`revalidate` segment config option](/docs/app/api-reference/file-conventions/route-segment-config#revalidate): - -```ts -export const revalidate = 60 -``` - ### Dynamic Functions Route Handlers can be used with dynamic functions from Next.js, like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers). diff --git a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx index 11ec48c01f062..29f99250e2e35 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx @@ -458,6 +458,7 @@ This will make the Next.js server console log ISR cache hits and misses. You can ## Caveats - ISR is only supported when using the Node.js runtime (default). +- ISR is not supported when creating a [Static Export](/docs/app/building-your-application/deploying/static-exports). - If you have multiple `fetch` requests in a statically rendered route, and each has a different `revalidate` frequency, the lowest time will be used for all requests. - Middleware won't be executed for on-demand ISR requests, meaning any path rewrites or logic in Middleware will not be applied. Ensure you are revalidating the exact path. For example, `/post/1` instead of a rewritten `/post-1`. diff --git a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx index 638087a90220f..87a818125484f 100644 --- a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx @@ -64,7 +64,7 @@ There are three subsets of server rendering: Static, Dynamic, and Streaming. {/* Static Rendering Diagram */} -With Static Rendering, routes are rendered at **build time**, or in the background after [data revalidation](/docs/app/building-your-application/data-fetching/caching-and-revalidating#revalidating-data). The result is cached and can be pushed to a [Content Delivery Network (CDN)](https://developer.mozilla.org/docs/Glossary/CDN). This optimization allows you to share the result of the rendering work between users and server requests. +With Static Rendering, routes are rendered at **build time**, or in the background after [data revalidation](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). The result is cached and can be pushed to a [Content Delivery Network (CDN)](https://developer.mozilla.org/docs/Glossary/CDN). This optimization allows you to share the result of the rendering work between users and server requests. Static rendering is useful when a route has data that is not personalized to the user and can be known at build time, such as a static blog post or a product page. @@ -86,7 +86,7 @@ Dynamic rendering is useful when a route has data that is personalized to the us #### Switching to Dynamic Rendering -During rendering, if a [dynamic function](#dynamic-functions) or [uncached data request](/docs/app/building-your-application/data-fetching/caching-and-revalidating) is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how dynamic functions and data caching affect whether a route is statically or dynamically rendered: +During rendering, if a [dynamic function](#dynamic-functions) or uncached data request is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how dynamic functions and data caching affect whether a route is statically or dynamically rendered: | Dynamic Functions | Data | Route | | ----------------- | ---------- | -------------------- | @@ -97,7 +97,7 @@ During rendering, if a [dynamic function](#dynamic-functions) or [uncached data In the table above, for a route to be fully static, all data must be cached. However, you can have a dynamically rendered route that uses both cached and uncached data fetches. -As a developer, you do not need to choose between static and dynamic rendering as Next.js will automatically choose the best rendering strategy for each route based on the features and APIs used. Instead, you choose when to [cache or revalidate specific data](/docs/app/building-your-application/data-fetching/caching-and-revalidating), and you may choose to [stream](#streaming) parts of your UI. +As a developer, you do not need to choose between static and dynamic rendering as Next.js will automatically choose the best rendering strategy for each route based on the features and APIs used. Instead, you choose when to [cache](/docs/app/building-your-application/data-fetching/fetching) or [revalidate specific data](/docs/app/building-your-application/data-fetching/incremental-static-regeneration), and you may choose to [stream](#streaming) parts of your UI. #### Dynamic Functions diff --git a/docs/02-app/01-building-your-application/03-rendering/03-composition-patterns.mdx b/docs/02-app/01-building-your-application/03-rendering/03-composition-patterns.mdx index 4a2a278b89a51..3f80c54c3dd32 100644 --- a/docs/02-app/01-building-your-application/03-rendering/03-composition-patterns.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/03-composition-patterns.mdx @@ -32,9 +32,9 @@ Here are some common patterns when working with Server Components: When fetching data on the server, there may be cases where you need to share data across different components. For example, you may have a layout and a page that depend on the same data. -Instead of using [React Context](https://react.dev/learn/passing-data-deeply-with-context) (which is not available on the server) or passing data as props, you can use [`fetch`](/docs/app/building-your-application/data-fetching/caching-and-revalidating#fetch-requests) or React's `cache` function to fetch the same data in the components that need it, without worrying about making duplicate requests for the same data. This is because React extends `fetch` to automatically memoize data requests, and the `cache` function can be used when `fetch` is not available. +Instead of using [React Context](https://react.dev/learn/passing-data-deeply-with-context) (which is not available on the server) or passing data as props, you can use `fetch` or React's `cache` function to fetch the same data in the components that need it, without worrying about making duplicate requests for the same data. This is because React extends `fetch` to automatically memoize data requests, and the `cache` function can be used when `fetch` is not available. -Learn more about [memoization](/docs/app/building-your-application/caching#request-memoization) in React. +[View an example](/docs/app/building-your-application/data-fetching/fetching#reusing-data-across-multiple-functions) of this pattern. ### Keeping Server-only Code out of the Client Environment @@ -404,7 +404,7 @@ export default function Layout({ children }) { If you fetch data in a Server Component, you may want to pass data down as props to Client Components. Props passed from the Server to Client Components need to be [serializable](https://react.dev/reference/react/use-server#serializable-parameters-and-return-values) by React. -If your Client Components depend on data that is not serializable, you can [fetch data on the client with a third party library](/docs/app/building-your-application/data-fetching/caching-and-revalidating#data-fetching-libraries-and-orms) or on the server via a [Route Handler](/docs/app/building-your-application/routing/route-handlers). +If your Client Components depend on data that is _not_ serializable, you can [fetch data on the client with a third party library](/docs/app/building-your-application/data-fetching/fetching#fetching-data-on-the-client) or on the server with a [Route Handler](/docs/app/building-your-application/routing/route-handlers). ## Interleaving Server and Client Components diff --git a/docs/02-app/01-building-your-application/07-configuring/01-typescript.mdx b/docs/02-app/01-building-your-application/07-configuring/01-typescript.mdx index 66e383ff8a15f..bd4c21bd03940 100644 --- a/docs/02-app/01-building-your-application/07-configuring/01-typescript.mdx +++ b/docs/02-app/01-building-your-application/07-configuring/01-typescript.mdx @@ -167,9 +167,9 @@ function Card({ href }: { href: Route | URL }) { The Next.js App Router has **enhanced type safety**. This includes: 1. **No serialization of data between fetching function and page**: You can `fetch` directly in components, layouts, and pages on the server. This data _does not_ need to be serialized (converted to a string) to be passed to the client side for consumption in React. Instead, since `app` uses Server Components by default, we can use values like `Date`, `Map`, `Set`, and more without any extra steps. Previously, you needed to manually type the boundary between server and client with Next.js-specific types. -2. **Streamlined data flow between components**: With the removal of `_app` in favor of root layouts, it is now easier to visualize the data flow between components and pages. Previously, data flowing between individual `pages` and `_app` were difficult to type and could introduce confusing bugs. With [colocated data fetching](/docs/app/building-your-application/data-fetching/caching-and-revalidating) in the App Router, this is no longer an issue. +2. **Streamlined data flow between components**: With the removal of `_app` in favor of root layouts, it is now easier to visualize the data flow between components and pages. Previously, data flowing between individual `pages` and `_app` were difficult to type and could introduce confusing bugs. With [colocated data fetching](/docs/app/building-your-application/data-fetching/fetching) in the App Router, this is no longer an issue. -[Data Fetching in Next.js](/docs/app/building-your-application/data-fetching/caching-and-revalidating) now provides as close to end-to-end type safety as possible without being prescriptive about your database or content provider selection. +[Data Fetching in Next.js](/docs/app/building-your-application/data-fetching/fetching) now provides as close to end-to-end type safety as possible without being prescriptive about your database or content provider selection. We're able to type the response data as you would expect with normal TypeScript. For example: diff --git a/docs/02-app/01-building-your-application/10-deploying/02-static-exports.mdx b/docs/02-app/01-building-your-application/10-deploying/02-static-exports.mdx index f84bbe6626993..8c8ca465f848f 100644 --- a/docs/02-app/01-building-your-application/10-deploying/02-static-exports.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/02-static-exports.mdx @@ -284,7 +284,7 @@ Features that require a Node.js server, or dynamic logic that cannot be computed - [Redirects](/docs/app/api-reference/next-config-js/redirects) - [Headers](/docs/app/api-reference/next-config-js/headers) - [Middleware](/docs/app/building-your-application/routing/middleware) -- [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/caching-and-revalidating) +- [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration) - [Image Optimization](/docs/app/building-your-application/optimizing/images) with the default `loader` - [Draft Mode](/docs/app/building-your-application/configuring/draft-mode) - [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) diff --git a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx index 162b8acad7d47..7d26326b0f25e 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx @@ -385,7 +385,7 @@ export default function HomePage({ recentPosts }) { - Create a new `app/page.tsx` file inside the `app` directory. This is a Server Component by default. - Import the `home-page.tsx` Client Component into the page. -- If you were fetching data in `pages/index.js`, move the data fetching logic directly into the Server Component using the new [data fetching APIs](/docs/app/building-your-application/data-fetching/caching-and-revalidating). See the [data fetching upgrade guide](#step-6-migrating-data-fetching-methods) for more details. +- If you were fetching data in `pages/index.js`, move the data fetching logic directly into the Server Component using the new [data fetching APIs](/docs/app/building-your-application/data-fetching/fetching). See the [data fetching upgrade guide](#step-6-migrating-data-fetching-methods) for more details. ```tsx filename="app/page.tsx" switcher // Import your Client Component @@ -548,9 +548,9 @@ export default function Dashboard({ projects }) { } ``` -In the `app` directory, we can colocate our data fetching inside our React components using [Server Components](/docs/app/building-your-application/rendering/server-components). This allows us to send less JavaScript to the client, while maintaining the rendered HTML from the server. +In the App Router, we can colocate our data fetching inside our React components using [Server Components](/docs/app/building-your-application/rendering/server-components). This allows us to send less JavaScript to the client, while maintaining the rendered HTML from the server. -By setting the `cache` option to `no-store`, we can indicate that the fetched data should [never be cached](/docs/app/building-your-application/data-fetching/caching-and-revalidating). This is similar to `getServerSideProps` in the `pages` directory. +By setting the `cache` option to `no-store`, we can indicate that the fetched data should [never be cached](/docs/app/building-your-application/data-fetching/fetching). This is similar to `getServerSideProps` in the `pages` directory. ```tsx filename="app/dashboard/page.tsx" switcher // `app` directory @@ -870,7 +870,7 @@ export async function GET(request: Request) {} export async function GET(request) {} ``` -> **Good to know**: If you previously used API routes to call an external API from the client, you can now use [Server Components](/docs/app/building-your-application/rendering/server-components) instead to securely fetch data. Learn more about [data fetching](/docs/app/building-your-application/data-fetching/caching-and-revalidating). +> **Good to know**: If you previously used API routes to call an external API from the client, you can now use [Server Components](/docs/app/building-your-application/rendering/server-components) instead to securely fetch data. Learn more about [data fetching](/docs/app/building-your-application/data-fetching/fetching). ### Step 7: Styling diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index c07bef8528df6..75cea804350fd 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -60,7 +60,7 @@ const headersList = headers() #### Usage with Data Fetching -`headers()` can be used in combination with [Suspense for Data Fetching](/docs/app/building-your-application/data-fetching/caching-and-revalidating). +`headers()` can be used in combination with React `Suspense`: ```jsx filename="app/page.js" import { Suspense } from 'react' diff --git a/docs/02-app/02-api-reference/05-next-config-js/incrementalCacheHandlerPath.mdx b/docs/02-app/02-api-reference/05-next-config-js/incrementalCacheHandlerPath.mdx index a30dbb6dcf96c..091e5b875eedb 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/incrementalCacheHandlerPath.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/incrementalCacheHandlerPath.mdx @@ -4,7 +4,11 @@ nav_title: cacheHandler description: Configure the Next.js cache used for storing and revalidating data to use any external service like Redis, Memcached, or others. --- -In Next.js, the [default cache handler](/docs/app/building-your-application/data-fetching/caching-and-revalidating) for the Pages and App Router uses the filesystem cache. This requires no configuration, however, you can customize the cache handler by using the `cacheHandler` field in `next.config.js`. +Caching and revalidating pages (with Incremental Static Regeneration) use the same shared cache. When [deploying to Vercel](https://vercel.com/docs/incremental-static-regeneration?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), the ISR cache is automatically persisted to durable storage. + +When self-hosting, the ISR cache is stored to the filesystem (on disk) on your Next.js server. This works automatically when self-hosting using both the Pages and App Router. + +You can configure the Next.js cache location if you want to persist cached pages and data to durable storage, or share the cache across multiple containers or instances of your Next.js application. ```js filename="next.config.js" module.exports = { @@ -43,7 +47,7 @@ Returns `Promise`. | --------- | -------- | ---------------------------- | | `tag` | `string` | The cache tag to revalidate. | -Returns `Promise`. Learn more about [revalidating data](/docs/app/building-your-application/data-fetching/caching-and-revalidating) or the [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) function. +Returns `Promise`. Learn more about [revalidating data](/docs/app/building-your-application/data-fetching/incremental-static-regeneration) or the [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) function. **Good to know:** diff --git a/docs/02-app/index.mdx b/docs/02-app/index.mdx index b188a536e9096..a2cd16a4a216d 100644 --- a/docs/02-app/index.mdx +++ b/docs/02-app/index.mdx @@ -66,7 +66,8 @@ Yes. You can view [Next.js Commerce](https://vercel.com/templates/next.js/nextjs ## Learn More - [Routing Fundamentals](/docs/app/building-your-application/routing) -- [Data Fetching, Caching, and Revalidating](/docs/app/building-your-application/data-fetching/caching-and-revalidating) +- [Data Fetching and Caching](/docs/app/building-your-application/data-fetching/fetching) +- [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration) - [Forms and Mutations](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) - [Caching](/docs/app/building-your-application/caching) - [Rendering Fundamentals](/docs/app/building-your-application/rendering) From 9ccbf9797e77dc03640448d7bb535eea8a7c116e Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 18:57:41 -0500 Subject: [PATCH 4/6] Fix broken links --- .../10-deploying/01-production-checklist.mdx | 2 +- .../05-next-config-js/devIndicators.mdx | 2 +- .../01-routing/07-api-routes.mdx | 2 +- .../03-data-fetching/01-get-static-props.mdx | 2 +- .../03-functions/get-static-paths.mdx | 4 +- .../03-functions/get-static-props.mdx | 38 +++++++++---------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx index f2b3eb4f8e916..efc0a236093f4 100644 --- a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx @@ -60,7 +60,7 @@ While building your application, we recommend using the following features to en -- **[Server Components](/docs/app/building-your-application/data-fetching/fetching#should-i-fetch-data-on-the-server-or-the-client):** Leverage the benefits of fetching data on the server using Server Components. +- **[Server Components](/docs/app/building-your-application/data-fetching/fetching):** Leverage the benefits of fetching data on the server using Server Components. - **[Route Handlers](/docs/app/building-your-application/routing/route-handlers):** Use Route Handlers to access your backend resources from Client Components. But do not call Route Handlers from Server Components to avoid an additional server request. - **[Streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming):** Use Loading UI and React Suspense to progressively send UI from the server to the client, and prevent the whole route from blocking while data is being fetched. - **[Parallel Data Fetching](/docs/app/building-your-application/data-fetching/fetching#parallel-and-sequential-data-fetching):** Reduce network waterfalls by fetching data in parallel, where appropriate. Also, consider [preloading data](/docs/app/building-your-application/data-fetching/fetching#preloading-data) where appropriate. diff --git a/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx b/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx index e80c89c10270f..ef2b5febc8bb2 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx @@ -105,7 +105,7 @@ Route (app) Size First Load JS There are two reasons a route might opt out of static rendering: - The presence of [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions) which rely on runtime information. -- An [uncached data request](/docs/app/building-your-application/data-fetching/fetching#fetch-api). +- An [uncached data request](/docs/app/building-your-application/data-fetching/fetching), like a call to an ORM or database driver. Check your route for any of these conditions, and if you are not able to statically render the route, then consider using [`loading.js`](/docs/app/api-reference/file-conventions/loading) or [``](https://react.dev/reference/react/Suspense) to leverage [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming#what-is-streaming). diff --git a/docs/03-pages/01-building-your-application/01-routing/07-api-routes.mdx b/docs/03-pages/01-building-your-application/01-routing/07-api-routes.mdx index e31e09e175381..f7fa6782f16d4 100644 --- a/docs/03-pages/01-building-your-application/01-routing/07-api-routes.mdx +++ b/docs/03-pages/01-building-your-application/01-routing/07-api-routes.mdx @@ -178,7 +178,7 @@ The included helpers are: - `res.json(body)` - Sends a JSON response. `body` must be a [serializable object](https://developer.mozilla.org/docs/Glossary/Serialization) - `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer` - `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "307" "Temporary redirect". -- `res.revalidate(urlPath)` - [Revalidate a page on demand](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) using `getStaticProps`. `urlPath` must be a `string`. +- `res.revalidate(urlPath)` - [Revalidate a page on demand](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) using `getStaticProps`. `urlPath` must be a `string`. ### Setting the status code of a response diff --git a/docs/03-pages/01-building-your-application/03-data-fetching/01-get-static-props.mdx b/docs/03-pages/01-building-your-application/03-data-fetching/01-get-static-props.mdx index 9f889c268c445..40bf5bb685b2c 100644 --- a/docs/03-pages/01-building-your-application/03-data-fetching/01-get-static-props.mdx +++ b/docs/03-pages/01-building-your-application/03-data-fetching/01-get-static-props.mdx @@ -61,7 +61,7 @@ You should use `getStaticProps` if: - `getStaticProps` runs in the background when using [`fallback: true`](/docs/pages/api-reference/functions/get-static-paths#fallback-true) - `getStaticProps` is called before initial render when using [`fallback: blocking`](/docs/pages/api-reference/functions/get-static-paths#fallback-blocking) - `getStaticProps` runs in the background when using `revalidate` -- `getStaticProps` runs on-demand in the background when using [`revalidate()`](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) +- `getStaticProps` runs on-demand in the background when using [`revalidate()`](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) When combined with [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration), `getStaticProps` will run in the background while the stale page is being revalidated, and the fresh page served to the browser. diff --git a/docs/03-pages/02-api-reference/03-functions/get-static-paths.mdx b/docs/03-pages/02-api-reference/03-functions/get-static-paths.mdx index 50c50e389f9fb..724bda1de3a1b 100644 --- a/docs/03-pages/02-api-reference/03-functions/get-static-paths.mdx +++ b/docs/03-pages/02-api-reference/03-functions/get-static-paths.mdx @@ -253,7 +253,7 @@ export default Post | Version | Changes | | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `v13.4.0` | [App Router](/docs/app/building-your-application/data-fetching) is now stable with simplified data fetching, including [`generateStaticParams()`](/docs/app/api-reference/functions/generate-static-params) | -| `v12.2.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) is stable. | -| `v12.1.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) added (beta). | +| `v12.2.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) is stable. | +| `v12.1.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) added (beta). | | `v9.5.0` | Stable [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration) | | `v9.3.0` | `getStaticPaths` introduced. | diff --git a/docs/03-pages/02-api-reference/03-functions/get-static-props.mdx b/docs/03-pages/02-api-reference/03-functions/get-static-props.mdx index 09457b4126352..413f482dc619f 100644 --- a/docs/03-pages/02-api-reference/03-functions/get-static-props.mdx +++ b/docs/03-pages/02-api-reference/03-functions/get-static-props.mdx @@ -46,16 +46,16 @@ You can import modules in top-level scope for use in `getStaticProps`. Imports u The `context` parameter is an object containing the following keys: -| Name | Description | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `params` | Contains the route parameters for pages using [dynamic routes](/docs/pages/building-your-application/routing/dynamic-routes). For example, if the page name is `[id].js`, then `params` will look like `{ id: ... }`. You should use this together with `getStaticPaths`, which we'll explain later. | -| `preview` | (Deprecated for `draftMode`) `preview` is `true` if the page is in the [Preview Mode](/docs/pages/building-your-application/configuring/preview-mode) and `false` otherwise. | -| `previewData` | (Deprecated for `draftMode`) The [preview](/docs/pages/building-your-application/configuring/preview-mode) data set by `setPreviewData`. | -| `draftMode` | `draftMode` is `true` if the page is in the [Draft Mode](/docs/pages/building-your-application/configuring/draft-mode) and `false` otherwise. | -| `locale` | Contains the active locale (if enabled). | -| `locales` | Contains all supported locales (if enabled). | -| `defaultLocale` | Contains the configured default locale (if enabled). | -| `revalidateReason` | Provides a reason for why the function was called. Can be one of: "build" (run at build time), "stale" (revalidate period expired, or running in [development mode](/docs/pages/building-your-application/data-fetching/get-static-props#runs-on-every-request-in-development)), "on-demand" (triggered via [on-demand revalidation](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation)) | +| Name | Description | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | Contains the route parameters for pages using [dynamic routes](/docs/pages/building-your-application/routing/dynamic-routes). For example, if the page name is `[id].js`, then `params` will look like `{ id: ... }`. You should use this together with `getStaticPaths`, which we'll explain later. | +| `preview` | (Deprecated for `draftMode`) `preview` is `true` if the page is in the [Preview Mode](/docs/pages/building-your-application/configuring/preview-mode) and `false` otherwise. | +| `previewData` | (Deprecated for `draftMode`) The [preview](/docs/pages/building-your-application/configuring/preview-mode) data set by `setPreviewData`. | +| `draftMode` | `draftMode` is `true` if the page is in the [Draft Mode](/docs/pages/building-your-application/configuring/draft-mode) and `false` otherwise. | +| `locale` | Contains the active locale (if enabled). | +| `locales` | Contains all supported locales (if enabled). | +| `defaultLocale` | Contains the configured default locale (if enabled). | +| `revalidateReason` | Provides a reason for why the function was called. Can be one of: "build" (run at build time), "stale" (revalidate period expired, or running in [development mode](/docs/pages/building-your-application/data-fetching/get-static-props#runs-on-every-request-in-development)), "on-demand" (triggered via [on-demand revalidation](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath)) | ## getStaticProps return values @@ -218,12 +218,12 @@ export default Blog ## Version History -| Version | Changes | -| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `v13.4.0` | [App Router](/docs/app/building-your-application/data-fetching) is now stable with simplified data fetching | -| `v12.2.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) is stable. | -| `v12.1.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation) added (beta). | -| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | -| `v10.0.0` | `fallback: 'blocking'` return option added. | -| `v9.5.0` | Stable [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration) | -| `v9.3.0` | `getStaticProps` introduced. | +| Version | Changes | +| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `v13.4.0` | [App Router](/docs/app/building-your-application/data-fetching) is now stable with simplified data fetching | +| `v12.2.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) is stable. | +| `v12.1.0` | [On-Demand Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath) added (beta). | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v10.0.0` | `fallback: 'blocking'` return option added. | +| `v9.5.0` | Stable [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration) | +| `v9.3.0` | `getStaticProps` introduced. | From 80c6c79cd9b2dd556b4a750b8e9b1cdde1a405f5 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 19:57:55 -0500 Subject: [PATCH 5/6] Fix code snippet --- .../04-incremental-static-regeneration.mdx | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx index 29f99250e2e35..9bf146c4e68b5 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx @@ -282,11 +282,11 @@ If you are using an ORM or connecting to a database, you can use `unstable_cache ```jsx import { unstable_cache } from 'next/cache' +import { db, posts } from '@/lib/db' const getCachedPosts = unstable_cache( async () => { - let posts = await sql`SELECT * FROM posts WHERE published = true` - return posts + return await db.select().from(posts) }, ['posts'], { revalidate: 3600, tags: ['posts'] } @@ -397,41 +397,11 @@ When self-hosting, the ISR cache is stored to the filesystem (on disk) on your N You can configure the Next.js cache location if you want to persist cached pages and data to durable storage, or share the cache across multiple containers or instances of your Next.js application. [Learn more](/docs/app/building-your-application/deploying#caching-and-isr). -## Error handling and revalidation - -If there is an error inside `getStaticProps` when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling `getStaticProps`. - -```jsx -export async function getStaticProps() { - // If this request throws an uncaught error, Next.js will - // not invalidate the currently shown page and - // retry getStaticProps on the next request. - let res = await fetch('https://.../posts') - let posts = await res.json() - - if (!res.ok) { - // If there is a server error, you might want to - // throw an error instead of returning so that the cache is not updated - // until the next successful request. - throw new Error(`Failed to fetch posts, received status ${res.status}`) - } - - // If the request was successful, return the posts - // and revalidate every 10 seconds. - return { - props: { - posts, - }, - revalidate: 10, - } -} -``` - ## Troubleshooting ### Debugging cached data in local development -If you are using the `fetch` API, you can add additional logging to understand which requests are cached or uncached. Learn more about the `logging` option. +If you are using the `fetch` API, you can add additional logging to understand which requests are cached or uncached. [Learn more about the `logging` option](/docs/app/api-reference/next-config-js/logging). ```jsx filename="next.config.js" module.exports = { From 66f111cba82a8a344c3f050eb6417facb9009a76 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Sun, 25 Aug 2024 19:58:50 -0500 Subject: [PATCH 6/6] Fix --- .../04-incremental-static-regeneration.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx index 9bf146c4e68b5..71f2d6a4fc01f 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/04-incremental-static-regeneration.mdx @@ -379,10 +379,9 @@ export async function getStaticProps({ params }) { return { props: { post }, - // Next.js will attempt to regenerate the page: - // - When a request comes in - // - At most once every 60 seconds - revalidate: 60, // In seconds + // Next.js will invalidate the cache when a + // request comes in, at most once every 60 seconds. + revalidate: 60, } } ```