Update docs for revalidatePath fix (#55083)

This updates docs for the fixes landed in
https://github.com/vercel/next.js/pull/53321 related to
`revalidatePath`.

Fixes: https://github.com/vercel/next.js/issues/49387

---------

Co-authored-by: Lee Robinson <me@leerob.io>
This commit is contained in:
JJ Kasper 2023-09-12 11:28:16 -07:00 committed by GitHub
parent 45fbd4db14
commit c2f587640a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 15 deletions

View file

@ -98,7 +98,7 @@ Set the cache lifetime of a resource (in seconds).
fetch(`https://...`, { next: { tags: ['collection'] } })
```
Set the cache tags of a resource. Data can then be revalidated on-demand using [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag).
Set the cache tags of a resource. Data can then be revalidated on-demand using [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag). The max length for a custom tag is 256 characters.
## Version History

View file

@ -8,16 +8,16 @@ description: API Reference for the revalidatePath function.
> **Good to know**:
>
> - `revalidatePath` is available in both [Node.js and Edge runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes).
> - `revalidatePath` will revalidate _all segments_ under a dynamic route segment. For example, if you have a dynamic segment `/product/[id]` and you call `revalidatePath('/product/[id]')`, then all segments under `/product/[id]` will be revalidated as requested.
> - `revalidatePath` only invalidates the cache when the path is next visited. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
> - `revalidatePath` only invalidates the cache when the included path is next visited. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
## Parameters
```tsx
revalidatePath(path: string): void;
revalidatePath(path: string, type?: 'page' | 'layout'): void;
```
- `path`: A string representing the filesystem path associated with the data you want to revalidate. This is **not** the literal route segment (e.g. `/product/123`) but instead the path on the filesystem (e.g. `/product/[id]`).
- `path`: A string representing the filesystem path associated with the data you want to revalidate. This is the literal route segment (for example, `/product/123`) not the path on the filesystem `/product/[slug]/page`. Must be less than 1024 characters.
- `type`: (optional) `'page'` or `'layout'` string to change the type of path to revalidate.
## Returns
@ -25,6 +25,33 @@ revalidatePath(path: string): void;
## Examples
### Revalidating A Specific URL
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/post-1')
```
This will revalidate one specific URL on the next page visit.
### Revalidating A Page Path
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'page')
```
This will revalidate any URL that matches the provided `page` file on the next page visit. This will _not_ invalidate pages beneath the specific page. For example, `/blog/[slug]` won't invalidate `/blog/[slug]/[author]`.
### Revalidating A Layout Path
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'layout')
```
This will revalidate any URL that matches the provided `layout` file on the next page visit. This will cause pages beneath with the same layout to revalidate on the next visit. For example, in the above case, `/blog/[slug]/[another]` would also revalidate on the next visit.
### Server Action
```ts filename="app/actions.ts" switcher

View file

@ -16,7 +16,7 @@ description: API Reference for the revalidateTag function.
revalidateTag(tag: string): void;
```
- `tag`: A string representing the cache tag associated with the data you want to revalidate.
- `tag`: A string representing the cache tag associated with the data you want to revalidate. Must be less than or equal to 256 characters.
You can add tags to `fetch` as follows:

View file

@ -12,6 +12,8 @@ export const NEXT_CACHE_REVALIDATED_TAGS_HEADER = 'x-next-revalidated-tags'
export const NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER =
'x-next-revalidate-tag-token'
export const NEXT_CACHE_TAG_MAX_LENGTH = 256
export const NEXT_CACHE_SOFT_TAG_MAX_LENGTH = 1024
export const NEXT_CACHE_IMPLICIT_TAG_ID = '_N_T_'
// in seconds

View file

@ -3,11 +3,45 @@ import type * as ServerHooks from '../../client/components/hooks-server-context'
import { AppRenderSpan, NextNodeServerSpan } from './trace/constants'
import { getTracer, SpanKind } from './trace/tracer'
import { CACHE_ONE_YEAR, NEXT_CACHE_IMPLICIT_TAG_ID } from '../../lib/constants'
import {
CACHE_ONE_YEAR,
NEXT_CACHE_IMPLICIT_TAG_ID,
NEXT_CACHE_TAG_MAX_LENGTH,
} from '../../lib/constants'
import * as Log from '../../build/output/log'
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
export function validateTags(tags: any[], description: string) {
const validTags: string[] = []
const invalidTags: Array<{
tag: any
reason: string
}> = []
for (const tag of tags) {
if (typeof tag !== 'string') {
invalidTags.push({ tag, reason: 'invalid type, must be a string' })
} else if (tag.length > NEXT_CACHE_TAG_MAX_LENGTH) {
invalidTags.push({
tag,
reason: `exceeded max length of ${NEXT_CACHE_TAG_MAX_LENGTH}`,
})
} else {
validTags.push(tag)
}
}
if (invalidTags.length > 0) {
console.warn(`Warning: invalid tags passed to ${description}: `)
for (const { tag, reason } of invalidTags) {
console.log(`tag: "${tag}" ${reason}`)
}
}
return validTags
}
const getDerivedTags = (pathname: string): string[] => {
const derivedTags: string[] = [`/layout`]
@ -194,7 +228,10 @@ export function patchFetch({
// RequestInit doesn't keep extra fields e.g. next so it's
// only available if init is used separate
let curRevalidate = getNextField('revalidate')
const tags: string[] = getNextField('tags') || []
const tags: string[] = validateTags(
getNextField('tags') || [],
`fetch ${input.toString()}`
)
if (Array.isArray(tags)) {
if (!staticGenerationStore.tags) {

View file

@ -1,11 +1,26 @@
import { revalidateTag } from './revalidate-tag'
import { NEXT_CACHE_IMPLICIT_TAG_ID } from '../../../lib/constants'
import { isDynamicRoute } from '../../../shared/lib/router/utils'
import {
NEXT_CACHE_IMPLICIT_TAG_ID,
NEXT_CACHE_SOFT_TAG_MAX_LENGTH,
} from '../../../lib/constants'
export function revalidatePath(path: string, type?: 'layout' | 'page') {
path = `${NEXT_CACHE_IMPLICIT_TAG_ID}${path}`
export function revalidatePath(originalPath: string, type?: 'layout' | 'page') {
if (originalPath.length > NEXT_CACHE_SOFT_TAG_MAX_LENGTH) {
console.warn(
`Warning: revalidatePath received "${originalPath}" which exceeded max length of ${NEXT_CACHE_SOFT_TAG_MAX_LENGTH}. See more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`
)
return
}
let normalizedPath = `${NEXT_CACHE_IMPLICIT_TAG_ID}${originalPath}`
if (type) {
path += `${path.endsWith('/') ? '' : '/'}${type}`
normalizedPath += `${normalizedPath.endsWith('/') ? '' : '/'}${type}`
} else if (isDynamicRoute(originalPath)) {
console.warn(
`Warning: a dynamic page path "${originalPath}" was passed to "revalidatePath" without the "page" argument. This has no affect by default, see more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`
)
}
return revalidateTag(path)
return revalidateTag(normalizedPath)
}

View file

@ -4,7 +4,7 @@ import {
StaticGenerationAsyncStorage,
} from '../../../client/components/static-generation-async-storage.external'
import { CACHE_ONE_YEAR } from '../../../lib/constants'
import { addImplicitTags } from '../../lib/patch-fetch'
import { addImplicitTags, validateTags } from '../../lib/patch-fetch'
type Callback = (...args: any[]) => Promise<any>
@ -56,7 +56,10 @@ export function unstable_cache<T extends Callback>(
isStaticGeneration: !!store?.isStaticGeneration,
},
async () => {
const tags = options.tags || []
const tags = validateTags(
options.tags || [],
`unstable_cache ${cb.toString()}`
)
if (Array.isArray(tags) && store) {
if (!store.tags) {