diff --git a/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx b/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx index b08e97a035f6df..69eb8a8f369c7b 100644 --- a/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx +++ b/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx @@ -6,10 +6,11 @@ related: description: Learn more about the features mentioned in this page by reading the API Reference. links: - app/api-reference/functions/fetch - - app/api-reference/functions/unstable_cache - - app/api-reference/functions/revalidatePath + - app/api-reference/functions/cacheTag - app/api-reference/functions/revalidateTag - app/api-reference/functions/updateTag + - app/api-reference/functions/revalidatePath + - app/api-reference/functions/unstable_cache --- Caching is a technique for storing the result of data fetching and other computations so that future requests for the same data can be served faster, without doing the work again. While revalidation allows you to update cache entries without having to rebuild your entire application. @@ -17,10 +18,11 @@ Caching is a technique for storing the result of data fetching and other computa Next.js provides a few APIs to handle caching and revalidation. This guide will walk you through when and how to use them. - [`fetch`](#fetch) -- [`unstable_cache`](#unstable_cache) -- [`revalidatePath`](#revalidatepath) +- [`cacheTag`](#cachetag) - [`revalidateTag`](#revalidatetag) - [`updateTag`](#updatetag) +- [`revalidatePath`](#revalidatepath) +- [`unstable_cache`](#unstable_cache) (Legacy) ## `fetch` @@ -56,103 +58,65 @@ export default async function Page() { This will revalidate the data after a specified amount of seconds. -See the [`fetch` API reference](/docs/app/api-reference/functions/fetch) to learn more. - -## `unstable_cache` +You can also tag `fetch` requests to enable on-demand cache invalidation: -`unstable_cache` allows you to cache the result of database queries and other async functions. To use it, wrap `unstable_cache` around the function. For example: - -```ts filename="app/lib/data.ts" switcher -import { db } from '@/lib/db' +```tsx filename="app/lib/data.ts" switcher export async function getUserById(id: string) { - return db - .select() - .from(users) - .where(eq(users.id, id)) - .then((res) => res[0]) + const data = await fetch(`https://...`, { + next: { + tags: ['user'], + }, + }) } ``` ```jsx filename="app/lib/data.js" switcher -import { db } from '@/lib/db' - export async function getUserById(id) { - return db - .select() - .from(users) - .where(eq(users.id, id)) - .then((res) => res[0]) + const data = await fetch(`https://...`, { + next: { + tags: ['user'], + }, + }) } ``` -```tsx filename="app/page.tsx" highlight={2,11,13} switcher -import { unstable_cache } from 'next/cache' -import { getUserById } from '@/app/lib/data' +See the [`fetch` API reference](/docs/app/api-reference/functions/fetch) to learn more. -export default async function Page({ - params, -}: { - params: Promise<{ userId: string }> -}) { - const { userId } = await params +## `cacheTag` - const getCachedUser = unstable_cache( - async () => { - return getUserById(userId) - }, - [userId] // add the user ID to the cache key - ) -} -``` +[`cacheTag`](/docs/app/api-reference/functions/cacheTag) allows you to tag cached data in [Cache Components](/docs/app/getting-started/cache-components) so it can be revalidated on-demand. Previously, cache tagging was limited to `fetch` requests, and caching other work required the experimental `unstable_cache` API. -```jsx filename="app/page.js" highlight={2,7,9} switcher -import { unstable_cache } from 'next/cache' -import { getUserById } from '@/app/lib/data' +With Cache Components, you can use the [`use cache`](/docs/app/api-reference/directives/use-cache) directive to cache any computation, and `cacheTag` to tag it. This works with database queries, file system operations, and other server-side work. -export default async function Page({ params }) { - const { userId } = await params +```tsx filename="app/lib/data.ts" switcher +import { cacheTag } from 'next/cache' - const getCachedUser = unstable_cache( - async () => { - return getUserById(userId) - }, - [userId] // add the user ID to the cache key - ) +export async function getProducts() { + 'use cache' + cacheTag('products') + + const products = await db.query('SELECT * FROM products') + return products } ``` -The function accepts a third optional object to define how the cache should be revalidated. It accepts: +```jsx filename="app/lib/data.js" switcher +import { cacheTag } from 'next/cache' -- `tags`: an array of tags used by Next.js to revalidate the cache. -- `revalidate`: the number of seconds after cache should be revalidated. +export async function getProducts() { + 'use cache' + cacheTag('products') -```tsx filename="app/page.tsx" highlight={6-9} switcher -const getCachedUser = unstable_cache( - async () => { - return getUserById(userId) - }, - [userId], - { - tags: ['user'], - revalidate: 3600, - } -) + const products = await db.query('SELECT * FROM products') + return products +} ``` -```jsx filename="app/page.js" highlight={6-9} switcher -const getCachedUser = unstable_cache( - async () => { - return getUserById(userId) - }, - [userId], - { - tags: ['user'], - revalidate: 3600, - } -) -``` +Once tagged, you can use [`revalidateTag`](#revalidatetag) or [`updateTag`](#updatetag) to invalidate the cache entry for products. -See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unstable_cache) to learn more. +> **Good to know**: `cacheTag` is used with [Cache Components](/docs/app/getting-started/cache-components) and the [`use cache`](/docs/app/api-reference/directives/use-cache) directive. It expands the caching and revalidation story beyond `fetch`. + +See the [`cacheTag` API reference](/docs/app/api-reference/functions/cacheTag) to learn more. ## `revalidateTag` @@ -161,55 +125,7 @@ See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unsta - **With `profile="max"`**: Uses stale-while-revalidate semantics, serving stale content while fetching fresh content in the background - **Without the second argument**: Legacy behavior that immediately expires the cache (deprecated) -To use it with `fetch`, start by tagging the function with the `next.tags` option: - -```tsx filename="app/lib/data.ts" highlight={3-5} switcher -export async function getUserById(id: string) { - const data = await fetch(`https://...`, { - next: { - tags: ['user'], - }, - }) -} -``` - -```jsx filename="app/lib/data.js" highlight={3-5} switcher -export async function getUserById(id) { - const data = await fetch(`https://...`, { - next: { - tags: ['user'], - }, - }) -} -``` - -Alternatively, you can mark an `unstable_cache` function with the `tags` option: - -```tsx filename="app/lib/data.ts" highlight={6-8} switcher -export const getUserById = unstable_cache( - async (id: string) => { - return db.query.users.findFirst({ where: eq(users.id, id) }) - }, - ['user'], // Needed if variables are not passed as parameters - { - tags: ['user'], - } -) -``` - -```jsx filename="app/lib/data.js" highlight={6-8} switcher -export const getUserById = unstable_cache( - async (id) => { - return db.query.users.findFirst({ where: eq(users.id, id) }) - }, - ['user'], // Needed if variables are not passed as parameters - { - tags: ['user'], - } -) -``` - -Then, call `revalidateTag` in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action: +After tagging your cached data, using [`fetch`](#fetch) with `next.tags`, or the [`cacheTag`](#cachetag) function, you may call `revalidateTag` in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action: ```tsx filename="app/lib/actions.ts" highlight={1,5} switcher import { revalidateTag } from 'next/cache' @@ -233,28 +149,6 @@ You can reuse the same tag in multiple functions to revalidate them all at once. See the [`revalidateTag` API reference](/docs/app/api-reference/functions/revalidateTag) to learn more. -## `revalidatePath` - -`revalidatePath` is used to revalidate a route and following an event. To use it, call it in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action: - -```tsx filename="app/lib/actions.ts" highlight={1} switcher -import { revalidatePath } from 'next/cache' - -export async function updateUser(id: string) { - // Mutate data - revalidatePath('/profile') -``` - -```jsx filename="app/lib/actions.js" highlight={1} switcher -import { revalidatePath } from 'next/cache' - -export async function updateUser(id) { - // Mutate data - revalidatePath('/profile') -``` - -See the [`revalidatePath` API reference](/docs/app/api-reference/functions/revalidatePath) to learn more. - ## `updateTag` `updateTag` is specifically designed for Server Actions to immediately expire cached data for read-your-own-writes scenarios. Unlike `revalidateTag`, it can only be used within Server Actions and immediately expires the cache entry. @@ -307,3 +201,123 @@ The key differences between `revalidateTag` and `updateTag`: - **`revalidateTag`**: In Server Actions and Route Handlers, supports stale-while-revalidate with `profile="max"` See the [`updateTag` API reference](/docs/app/api-reference/functions/updateTag) to learn more. + +## `revalidatePath` + +`revalidatePath` is used to revalidate a route and following an event. To use it, call it in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action: + +```tsx filename="app/lib/actions.ts" highlight={1} switcher +import { revalidatePath } from 'next/cache' + +export async function updateUser(id: string) { + // Mutate data + revalidatePath('/profile') +``` + +```jsx filename="app/lib/actions.js" highlight={1} switcher +import { revalidatePath } from 'next/cache' + +export async function updateUser(id) { + // Mutate data + revalidatePath('/profile') +``` + +See the [`revalidatePath` API reference](/docs/app/api-reference/functions/revalidatePath) to learn more. + +## `unstable_cache` + +> **Good to know**: `unstable_cache` is an experimental API. We recommend opting into [Cache Components](/docs/app/getting-started/cache-components) and replacing `unstable_cache` with the [`use cache`](/docs/app/api-reference/directives/use-cache) directive. See the [Cache Components documentation](/docs/app/getting-started/cache-components) for more details. + +`unstable_cache` allows you to cache the result of database queries and other async functions. To use it, wrap `unstable_cache` around the function. For example: + +```ts filename="app/lib/data.ts" switcher +import { db } from '@/lib/db' +export async function getUserById(id: string) { + return db + .select() + .from(users) + .where(eq(users.id, id)) + .then((res) => res[0]) +} +``` + +```jsx filename="app/lib/data.js" switcher +import { db } from '@/lib/db' + +export async function getUserById(id) { + return db + .select() + .from(users) + .where(eq(users.id, id)) + .then((res) => res[0]) +} +``` + +```tsx filename="app/page.tsx" highlight={2,11,13} switcher +import { unstable_cache } from 'next/cache' +import { getUserById } from '@/app/lib/data' + +export default async function Page({ + params, +}: { + params: Promise<{ userId: string }> +}) { + const { userId } = await params + + const getCachedUser = unstable_cache( + async () => { + return getUserById(userId) + }, + [userId] // add the user ID to the cache key + ) +} +``` + +```jsx filename="app/page.js" highlight={2,7,9} switcher +import { unstable_cache } from 'next/cache' +import { getUserById } from '@/app/lib/data' + +export default async function Page({ params }) { + const { userId } = await params + + const getCachedUser = unstable_cache( + async () => { + return getUserById(userId) + }, + [userId] // add the user ID to the cache key + ) +} +``` + +The function accepts a third optional object to define how the cache should be revalidated. It accepts: + +- `tags`: an array of tags used by Next.js to revalidate the cache. +- `revalidate`: the number of seconds after cache should be revalidated. + +```tsx filename="app/page.tsx" highlight={6-9} switcher +const getCachedUser = unstable_cache( + async () => { + return getUserById(userId) + }, + [userId], + { + tags: ['user'], + revalidate: 3600, + } +) +``` + +```jsx filename="app/page.js" highlight={6-9} switcher +const getCachedUser = unstable_cache( + async () => { + return getUserById(userId) + }, + [userId], + { + tags: ['user'], + revalidate: 3600, + } +) +``` + +See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unstable_cache) to learn more. diff --git a/docs/01-app/01-getting-started/15-route-handlers.mdx b/docs/01-app/01-getting-started/15-route-handlers.mdx index a1c21cf4a0f78c..f0298aca3eae6c 100644 --- a/docs/01-app/01-getting-started/15-route-handlers.mdx +++ b/docs/01-app/01-getting-started/15-route-handlers.mdx @@ -84,6 +84,46 @@ export async function GET() { > **Good to know**: Other supported HTTP methods are **not** cached, even if they are placed alongside a `GET` method that is cached, in the same file. +#### With Cache Components + +When using [Cache Components](/docs/app/getting-started/cache-components), you can use the [`use cache`](/docs/app/api-reference/directives/use-cache) directive to cache data fetching within your Route Handlers. Route Handlers are dynamic by default, but can be pre-rendered at build time if they don't use runtime or dynamic data. + +```ts filename="app/api/posts/route.ts" switcher +import { cacheTag } from 'next/cache' + +async function getPosts() { + 'use cache' + cacheTag('posts') + + const posts = await fetchPosts() + return posts +} + +export async function GET() { + const posts = await getPosts() + return Response.json(posts) +} +``` + +```js filename="app/api/posts/route.js" switcher +import { cacheTag } from 'next/cache' + +async function getPosts() { + 'use cache' + cacheTag('posts') + + const posts = await fetchPosts() + return posts +} + +export async function GET() { + const posts = await getPosts() + return Response.json(posts) +} +``` + +See the [Cache Components documentation](/docs/app/getting-started/cache-components#route-handlers-with-cache-components) for more details on caching strategies and revalidation. + ### Special Route Handlers Special Route Handlers like [`sitemap.ts`](/docs/app/api-reference/file-conventions/metadata/sitemap), [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`icon.tsx`](/docs/app/api-reference/file-conventions/metadata/app-icons), and other [metadata files](/docs/app/api-reference/file-conventions/metadata) remain static by default unless they use Dynamic APIs or dynamic config options. diff --git a/docs/01-app/01-getting-started/16-proxy.mdx b/docs/01-app/01-getting-started/16-proxy.mdx index 074680bcb00d63..236d066388da28 100644 --- a/docs/01-app/01-getting-started/16-proxy.mdx +++ b/docs/01-app/01-getting-started/16-proxy.mdx @@ -12,15 +12,19 @@ related: ## Proxy +> **Good to know**: Starting with Next.js 16, Middleware is now called Proxy to better reflect its purpose. The functionality remains the same. + Proxy allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. ### Use cases Some common scenarios where Proxy is effective include: -- Quick redirects after reading parts of the incoming request -- Rewriting to different pages based on A/B tests or experiments - Modifying headers for all pages or a subset of pages +- Rewriting to different pages based on A/B tests or experiments +- Programmatic redirects based on incoming request properties + +For simple redirects, consider using the [`redirects`](/docs/app/api-reference/config/next-config-js/redirects) configuration in `next.config.ts` first. Proxy should be used when you need access to request data or more complex logic. Proxy is _not_ intended for slow data fetching. While Proxy can be helpful for [optimistic checks](/docs/app/guides/authentication#optimistic-checks-with-proxy-optional) such as permission-based redirects, it should not be used as a full session management or authorization solution. diff --git a/docs/01-app/01-getting-started/18-upgrading.mdx b/docs/01-app/01-getting-started/18-upgrading.mdx index 892df99b563778..3649f79718c1d5 100644 --- a/docs/01-app/01-getting-started/18-upgrading.mdx +++ b/docs/01-app/01-getting-started/18-upgrading.mdx @@ -48,13 +48,6 @@ npm i next@canary The following features are currently available in canary: -**Caching**: - -- [`"use cache"`](/docs/app/api-reference/directives/use-cache) -- [`cacheLife`](/docs/app/api-reference/functions/cacheLife) -- [`cacheTag`](/docs/app/api-reference/functions/cacheTag) -- [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) - **Authentication**: - [`forbidden`](/docs/app/api-reference/functions/forbidden)