mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Adds `experimental.instantInsights.validationLevel` so an app can opt into instant validation for all routes by default. There are two public levels * `manual-warning`: currently the default. this mode won't enable instant validation except where you add `export const unstable_instant = true` into your Layouts, Pages, and Default files * `warning`: This mode will enable instant validation for all Page and Default files. You can override this with `export const unstable_instant = false` in Layouts, Pages, and Default files to tune what navigations are validated. In addition this change disables build time validation. There is a now undocumented way to get back to running build validation with * `experimental-error` * `experimental-manual-error` The build validation isn't ready for users because we need to overhaul the setup where you provide cookies and other sample values so we can run validation without a live session like we do in dev. To further support this the two remaining object properties that are also not ready for active use, `samples` and `from` are being updated to `unstable_samples` and `unstable_from` respectively. We will continue to iterate on build time validation but may remove these APIs or alter their semantics as we figure out a more ergonomic way to support this setup. In anticipation of multiple levels the object form of `instant` can be provided a level value `warning` or `experimental-error`. this latter option is similarly undocumented so documented behavior will only cover `warning` which is generally a synonym of true unless you are activating error by default
128 lines
4.7 KiB
Plaintext
128 lines
4.7 KiB
Plaintext
---
|
|
title: Cannot access `crypto.getRandomValue()`, `crypto.randomUUID()`, or another web or node crypto API that generates random values synchronously before other uncached data or `connection()` in a Server Component
|
|
---
|
|
|
|
## Why This Error Occurred
|
|
|
|
An API that produces a random value synchronously from the Web Crypto API or from Node's `crypto` package was used in a Server Component before accessing other uncached data through APIs like `fetch()` and native database drivers, or the `connection()` API. While typically random crypto values can be guarded behind Runtime data like `cookies()`, `headers()`, `params`, and `searchParams`, this particular route is configured for Runtime Prefetching which makes these APIs available as part of the prefetch request. Accessing random values synchronously without preceding it with uncached data or `await connection()` interferes with the framework's ability to produce a correct prefetch result.
|
|
|
|
## Possible Ways to Fix It
|
|
|
|
If the random crypto value is appropriate to be prefetched consider moving it into a Cache Component or Cache Function with the `"use cache"` directive.
|
|
|
|
If the random crypto value is intended to be generated on every user navigation consider whether an async API exists that achieves the same result. If not consider whether you can move the random crypto value generation later, behind other existing uncached data or Request data access. If there is no way to do this you can always precede the random crypto value generation with Request data access by using `await connection()`.
|
|
|
|
### Cache the token value
|
|
|
|
If you are generating a token to talk to a database that itself should be cached move the token generation inside the `"use cache"`.
|
|
|
|
Before:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
async function getCachedData(token: string, userId: string) {
|
|
"use cache"
|
|
return db.query(token, userId, ...)
|
|
}
|
|
|
|
export default async function Page({ params }) {
|
|
const { userId } = await params
|
|
const token = crypto.randomUUID()
|
|
const data = await getCachedData(token, userId);
|
|
return ...
|
|
}
|
|
```
|
|
|
|
After:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
async function getCachedData(userId: string) {
|
|
"use cache"
|
|
const token = crypto.randomUUID()
|
|
return db.query(token, userId, ...)
|
|
}
|
|
|
|
export default async function Page({ params }) {
|
|
const { userId } = await params
|
|
const data = await getCachedData(userId);
|
|
return ...
|
|
}
|
|
```
|
|
|
|
### Use an async API at request-time
|
|
|
|
If you require this random value to be unique per Request and an async version of the API exists switch to it instead. Also ensure that there is a parent Suspense boundary that defines a fallback UI Next.js can use while rendering this component on each Request.
|
|
|
|
Before:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
import { generateKeySync } from 'node:crypto'
|
|
|
|
export default async function Page({ params }) {
|
|
const { dataId } = await params
|
|
const data = await fetchData(dataId)
|
|
const key = generateKeySync('hmac', { ... })
|
|
const digestedData = await digestDataWithKey(data, key);
|
|
return ...
|
|
}
|
|
```
|
|
|
|
After:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
import { generateKey } from 'node:crypto'
|
|
|
|
export default async function Page({ params }) {
|
|
const { dataId } = await params
|
|
const data = await fetchData(dataId)
|
|
const key = await new Promise(resolve => generateKey('hmac', { ... }, key => resolve(key)))
|
|
const digestedData = await digestDataWithKey(data, key);
|
|
return ...
|
|
}
|
|
```
|
|
|
|
### Use `await connection()` at request-time
|
|
|
|
If you require this random value to be unique per Request and an async version of the API does not exist, call `await connection()`. Also ensure that there is a parent Suspense boundary that defines a fallback UI Next.js can use while rendering this component on each Request.
|
|
|
|
Before:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
export default async function Page({ params }) {
|
|
const { sessionId } = await params
|
|
const uuid = crypto.randomUUID()
|
|
return <RequestId sessionId={sessionId} id={uuid} />
|
|
}
|
|
```
|
|
|
|
After:
|
|
|
|
```jsx filename="app/page.js"
|
|
export const unstable_prefetch = 'force-runtime'
|
|
|
|
import { connection } from 'next/server'
|
|
|
|
export default async function Page({ params }) {
|
|
await connection()
|
|
const { sessionId } = await params
|
|
const uuid = crypto.randomUUID()
|
|
return <RequestId sessionId={sessionId} id={uuid} />
|
|
}
|
|
```
|
|
|
|
## Useful Links
|
|
|
|
- [`connection` function](/docs/app/api-reference/functions/connection)
|
|
- [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)
|
|
- [Node Crypto API](https://nodejs.org/docs/latest/api/crypto.html)
|
|
- [`Suspense` React API](https://react.dev/reference/react/Suspense)
|