Skip to content

Commit

Permalink
Fix react fetch deduping without next cache (vercel#50187)
Browse files Browse the repository at this point in the history
This ensures we don't reference the wrong fetch global bypassing react's
patched fetch so that it can properly fallback to react's fetch deduping
when next cache is disabled for a specific fetch.

x-ref: [slack
thread](https://vercel.slack.com/archives/C042LHPJ1NX/p1684518670927189?thread_ts=1682974724.765039&cid=C042LHPJ1NX)
fixes: vercel#50031
  • Loading branch information
ijjk authored and hydRAnger committed Jun 12, 2023
1 parent c5f033c commit 5c97a10
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { StaticGenerationAsyncStorage } from '../client/components/static-g
import '../server/require-hook'
import '../server/node-polyfill-fetch'
import '../server/node-polyfill-crypto'
import '../server/node-environment'
import chalk from 'next/dist/compiled/chalk'
import getGzipSize from 'next/dist/compiled/gzip-size'
import textTable from 'next/dist/compiled/text-table'
Expand Down Expand Up @@ -62,7 +63,6 @@ import { StaticGenerationAsyncStorageWrapper } from '../server/async-storage/sta
import { IncrementalCache } from '../server/lib/incremental-cache'
import { patchFetch } from '../server/lib/patch-fetch'
import { nodeFs } from '../server/lib/node-fs-methods'
import '../server/node-environment'
import * as ciEnvironment from '../telemetry/ci-info'

export type ROUTER_TYPE = 'pages' | 'app'
Expand Down
10 changes: 7 additions & 3 deletions packages/next/src/server/lib/patch-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ export function patchFetch({
serverHooks: typeof ServerHooks
staticGenerationAsyncStorage: StaticGenerationAsyncStorage
}) {
if (!(globalThis as any)._nextOriginalFetch) {
;(globalThis as any)._nextOriginalFetch = globalThis.fetch
}

if ((globalThis.fetch as any).__nextPatched) return

const { DynamicServerError } = serverHooks
const originFetch = globalThis.fetch
const originFetch: typeof fetch = (globalThis as any)._nextOriginalFetch

globalThis.fetch = async (
input: RequestInfo | URL,
Expand Down Expand Up @@ -527,8 +531,8 @@ export function patchFetch({
}
)
}
;(fetch as any).__nextGetStaticStore = () => {
;(globalThis.fetch as any).__nextGetStaticStore = () => {
return staticGenerationAsyncStorage
}
;(fetch as any).__nextPatched = true
;(globalThis.fetch as any).__nextPatched = true
}
4 changes: 2 additions & 2 deletions packages/next/src/server/node-polyfill-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// TODO: Remove use of `any` type.
// Polyfill fetch() in the Node.js environment

if (!(global as any).fetch) {
if (typeof fetch === 'undefined' && typeof globalThis.fetch === 'undefined') {
function getFetchImpl() {
return require('next/dist/compiled/undici')
}
Expand All @@ -17,7 +17,7 @@ if (!(global as any).fetch) {
}

// Due to limitation of global configuration, we have to do this resolution at runtime
;(global as any).fetch = (...args: any[]) => {
globalThis.fetch = (...args: any[]) => {
const fetchImpl = getFetchImpl()

// Undici does not support the `keepAlive` option,
Expand Down
32 changes: 32 additions & 0 deletions test/e2e/app-dir/app-static/app-static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ createNextDescribe(
}
})

it.each([
{
path: '/react-fetch-deduping-node',
},
{
path: '/react-fetch-deduping-edge',
},
])(
'should correctly de-dupe fetch without next cache $path',
async ({ path }) => {
for (let i = 0; i < 5; i++) {
const res = await next.fetch(path, {
redirect: 'manual',
})

expect(res.status).toBe(200)
const html = await res.text()
const $ = cheerio.load(html)

const data1 = $('#data-1').text()
const data2 = $('#data-2').text()

expect(data1).toBeTruthy()
expect(data1).toBe(data2)

await waitFor(250)
}
}
)

it.each([
{ pathname: '/unstable-cache-node' },
{ pathname: '/unstable-cache-edge' },
Expand Down Expand Up @@ -506,6 +536,8 @@ createNextDescribe(
'partial-gen-params-no-additional-slug/fr/second.html',
'partial-gen-params-no-additional-slug/fr/second.rsc',
'partial-gen-params/[lang]/[slug]/page.js',
'react-fetch-deduping-edge/page.js',
'react-fetch-deduping-node/page.js',
'route-handler-edge/revalidate-360/route.js',
'route-handler/post/route.js',
'route-handler/revalidate-360-isr/route.js',
Expand Down
29 changes: 29 additions & 0 deletions test/e2e/app-dir/app-static/app/react-fetch-deduping-edge/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const runtime = 'edge'

export default async function Page() {
const data1 = await fetch(
'https://next-data-api-endpoint.vercel.app/api/random?1',
{
next: {
revalidate: 0,
},
}
).then((res) => res.text())

const data2 = await fetch(
'https://next-data-api-endpoint.vercel.app/api/random?1',
{
next: {
revalidate: 0,
},
}
).then((res) => res.text())

return (
<>
<p>/react-fetch-deduping</p>
<p id="data-1">{data1}</p>
<p id="data-2">{data2}</p>
</>
)
}
27 changes: 27 additions & 0 deletions test/e2e/app-dir/app-static/app/react-fetch-deduping-node/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export default async function Page() {
const data1 = await fetch(
'https://next-data-api-endpoint.vercel.app/api/random?1',
{
next: {
revalidate: 0,
},
}
).then((res) => res.text())

const data2 = await fetch(
'https://next-data-api-endpoint.vercel.app/api/random?1',
{
next: {
revalidate: 0,
},
}
).then((res) => res.text())

return (
<>
<p>/react-fetch-deduping</p>
<p id="data-1">{data1}</p>
<p id="data-2">{data2}</p>
</>
)
}

0 comments on commit 5c97a10

Please sign in to comment.