Skip to content

Commit 3e743de

Browse files
authoredJan 4, 2024
feat: stop running middleware at the origin (#125)
1 parent a1eaca3 commit 3e743de

File tree

4 files changed

+55
-6
lines changed

4 files changed

+55
-6
lines changed
 

‎src/build/content/server.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { NetlifyPluginOptions } from '@netlify/build'
22
import glob from 'fast-glob'
33
import { existsSync } from 'node:fs'
44
import { cp, mkdir, readFile, readdir, readlink, symlink, writeFile } from 'node:fs/promises'
5-
import { join, resolve } from 'node:path'
5+
import { dirname, join, resolve } from 'node:path'
66
import { getPrerenderManifest } from '../config.js'
77
import { SERVER_HANDLER_DIR } from '../constants.js'
88

@@ -22,7 +22,18 @@ export const copyNextServerCode = async ({
2222

2323
await Promise.all(
2424
paths.map(async (path: string) => {
25-
await cp(join(srcDir, path), join(destDir, path), { recursive: true })
25+
const destPath = join(destDir, path)
26+
27+
// If this is the middleware manifest file, replace it with an empty
28+
// manifest to avoid running middleware again in the server handler.
29+
if (path === 'server/middleware-manifest.json') {
30+
await mkdir(dirname(destPath), { recursive: true })
31+
await writeFile(destPath, getEmptyMiddlewareManifest())
32+
33+
return
34+
}
35+
36+
await cp(join(srcDir, path), destPath, { recursive: true })
2637
}),
2738
)
2839
}
@@ -117,3 +128,19 @@ export const writeTagsManifest = async ({
117128
'utf-8',
118129
)
119130
}
131+
132+
/**
133+
* Generates an empty middleware manifest. We don't want to run middleware in
134+
* the server handler, because we'll run it upstream in an edge function. So
135+
* we patch the manifest to make it seem like there's no middleware configured.
136+
*/
137+
const getEmptyMiddlewareManifest = () => {
138+
const manifest = {
139+
sortedMiddleware: [],
140+
middleware: {},
141+
functions: {},
142+
version: 2,
143+
}
144+
145+
return JSON.stringify(manifest)
146+
}

‎tests/e2e/edge-middleware.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,15 @@ test('Runs edge middleware', async ({ page }) => {
1919
const h1 = page.locator('h1')
2020
await expect(h1).toHaveText('Other')
2121
})
22+
23+
test('Does not run edge middleware at the origin', async ({ page }) => {
24+
const res = await page.goto(`${ctx.url}/test/next`)
25+
26+
expect(await res?.headerValue('x-deno')).toBeTruthy()
27+
expect(await res?.headerValue('x-node')).toBeNull()
28+
29+
await expect(page).toHaveTitle('Simple Next App')
30+
31+
const h1 = page.locator('h1')
32+
await expect(h1).toHaveText('Message from middleware: hello')
33+
})

‎tests/fixtures/middleware/middleware.ts

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export function middleware(request: NextRequest) {
3434
response = NextResponse.json({ error: 'Error' }, { status: 500 })
3535
}
3636

37+
response.headers.append('Deno' in globalThis ? 'x-deno' : 'x-node', Date.now().toString())
38+
3739
response.headers.set('x-hello-from-middleware-res', 'hello')
3840
return response
3941
}

‎tests/utils/fixture.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { existsSync } from 'node:fs'
1313
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
1414
import { tmpdir } from 'node:os'
1515
import { basename, dirname, join, parse, relative, resolve } from 'node:path'
16+
import { env } from 'node:process'
1617
import { fileURLToPath } from 'node:url'
1718
import { v4 } from 'uuid'
1819
import {
@@ -59,15 +60,22 @@ function installDependencies(cwd: string) {
5960
export const createFixture = async (fixture: string, ctx: FixtureTestContext) => {
6061
ctx.cwd = await mkdtemp(join(tmpdir(), 'netlify-next-runtime-'))
6162
vi.spyOn(process, 'cwd').mockReturnValue(ctx.cwd)
62-
ctx.cleanup = [
63-
async () => {
63+
64+
ctx.cleanup = []
65+
66+
if (env.INTEGRATION_PERSIST) {
67+
console.log(
68+
`💾 Fixture '${fixture}' has been persisted at '${ctx.cwd}'. To clean up automatically, run tests without the 'INTEGRATION_PERSIST' environment variable.`,
69+
)
70+
} else {
71+
ctx.cleanup.push(async () => {
6472
try {
6573
await rm(ctx.cwd, { recursive: true, force: true })
6674
} catch {
6775
// noop
6876
}
69-
},
70-
]
77+
})
78+
}
7179

7280
try {
7381
const src = fileURLToPath(new URL(`../fixtures/${fixture}`, import.meta.url))

0 commit comments

Comments
 (0)
Please sign in to comment.