Skip to content

Commit d714bb3

Browse files
authored
feat: support isr reason for on request error (#68845)
### What Provide `revalidateReason` in the `errorContext` argument of `onRequestError` API #### API ```ts revalidateReason?: 'on-demand' | 'stale' | undefined ``` ### Why With this property, developer is able to determine if current error is attached to an ISR request. * `stale` means it revalidated due to the cache is expired * `on-demand` means it revalidated due to a user initiated revalidation, e.g. using `res.revalidate` to revalidate certain path ### Misc * We don't generate edge entry of instrumentation when there's no edge routes but instrumentation is presented Note: for PPR case we'll tackle it later with another PPR specific task, since we need to provide the render is a postpone render or just normal render Closes NDX-23
1 parent d50e041 commit d714bb3

File tree

22 files changed

+287
-5
lines changed

22 files changed

+287
-5
lines changed

packages/next/src/build/entries.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,12 @@ export async function createEntrypoints(
792792

793793
await Promise.all(promises)
794794

795+
// Optimization: If there's only one instrumentation hook in edge compiler, which means there's no edge server entry.
796+
// We remove the edge instrumentation entry from edge compiler as it can be pure server side.
797+
if (edgeServer.instrumentation && Object.keys(edgeServer).length === 1) {
798+
delete edgeServer.instrumentation
799+
}
800+
795801
return {
796802
client,
797803
server,

packages/next/src/build/templates/middleware.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ function errorHandledHandler(fn: AdapterOptions['handler']) {
4040
routerKind: 'Pages Router',
4141
routePath: '/middleware',
4242
routeType: 'middleware',
43+
revalidateReason: undefined,
4344
}
4445
)
4546

packages/next/src/server/api-utils/node/api-resolver.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ export async function apiResolver(
441441
routerKind: 'Pages Router',
442442
routePath: page || '',
443443
routeType: 'route',
444+
revalidateReason: undefined,
444445
})
445446

446447
if (err instanceof ApiError) {

packages/next/src/server/app-render/app-render.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ import { getServerActionRequestMetadata } from '../lib/server-action-request-met
132132
import { createInitialRouterState } from '../../client/components/router-reducer/create-initial-router-state'
133133
import { createMutableActionQueue } from '../../shared/lib/router/action-queue'
134134
import { prerenderAsyncStorage } from './prerender-async-storage.external'
135+
import { getRevalidateReason } from '../instrumentation/utils'
135136

136137
export type GetDynamicParamFromSegment = (
137138
// [slug] / [[slug]] / [...slug]
@@ -420,6 +421,7 @@ function createErrorContext(
420421
routePath: ctx.pagePath,
421422
routeType: ctx.isAction ? 'action' : 'render',
422423
renderSource,
424+
revalidateReason: getRevalidateReason(ctx.staticGenerationStore),
423425
}
424426
}
425427
/**

packages/next/src/server/base-server.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ import {
161161
} from './after/builtin-request-context'
162162
import { ENCODED_TAGS } from './stream-utils/encodedTags'
163163
import { NextRequestHint } from './web/adapter'
164+
import { getRevalidateReason } from './instrumentation/utils'
164165
import { RouteKind } from './route-kind'
165166
import type { RouteModule } from './route-modules/route-module'
166167

@@ -2520,17 +2521,18 @@ export default abstract class Server<
25202521
)
25212522
return null
25222523
} catch (err) {
2523-
// If this is during static generation, throw the error again.
2524-
if (isSSG) throw err
2525-
2526-
Log.error(err)
2527-
25282524
await this.instrumentationOnRequestError(err, req, {
25292525
routerKind: 'App Router',
25302526
routePath: pathname,
25312527
routeType: 'route',
2528+
revalidateReason: getRevalidateReason(renderOpts),
25322529
})
25332530

2531+
// If this is during static generation, throw the error again.
2532+
if (isSSG) throw err
2533+
2534+
Log.error(err)
2535+
25342536
// Otherwise, send a 500 response.
25352537
await sendResponse(req, res, handleInternalServerErrorResponse())
25362538

@@ -2579,6 +2581,10 @@ export default abstract class Server<
25792581
routerKind: 'Pages Router',
25802582
routePath: pathname,
25812583
routeType: 'render',
2584+
revalidateReason: getRevalidateReason({
2585+
isRevalidate: isSSG,
2586+
isOnDemandRevalidate: renderOpts.isOnDemandRevalidate,
2587+
}),
25822588
})
25832589
throw err
25842590
}

packages/next/src/server/instrumentation/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type RequestErrorContext = {
66
| 'react-server-components'
77
| 'react-server-components-payload'
88
| 'server-rendering'
9+
revalidateReason: 'on-demand' | 'stale' | undefined
910
// TODO: other future instrumentation context
1011
}
1112

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function getRevalidateReason(params: {
2+
isOnDemandRevalidate?: boolean
3+
isRevalidate?: boolean
4+
}): 'on-demand' | 'stale' | undefined {
5+
if (params.isOnDemandRevalidate) {
6+
return 'on-demand'
7+
}
8+
if (params.isRevalidate) {
9+
return 'stale'
10+
}
11+
return undefined
12+
}

packages/next/src/server/next-server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,8 @@ export default class NextNodeServer extends BaseServer<
984984
routePath: match.definition.page,
985985
routerKind: 'Pages Router',
986986
routeType: 'route',
987+
// Edge runtime does not support ISR
988+
revalidateReason: undefined,
987989
})
988990
throw apiError
989991
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function Page() {
2+
if (process.env.NEXT_PHASE !== 'phase-production-build') {
3+
throw new Error('app:on-demand')
4+
}
5+
return <p>{Date.now()}</p>
6+
}
7+
8+
export const revalidate = 1000
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function GET() {
2+
if (process.env.NEXT_PHASE !== 'phase-production-build') {
3+
throw new Error('app:route:on-demand')
4+
}
5+
return new Response('app:route')
6+
}
7+
8+
export const revalidate = 1000

0 commit comments

Comments
 (0)