Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#11930): rewritten api routes can correctly handle cors in dev mode #38937

Merged
merged 9 commits into from Jul 30, 2022
32 changes: 28 additions & 4 deletions packages/next/server/dev/hot-reloader.ts
Expand Up @@ -49,7 +49,8 @@ import ws from 'next/dist/compiled/ws'
import { promises as fs } from 'fs'
import { getPageStaticInfo } from '../../build/analysis/get-page-static-info'
import { serverComponentRegex } from '../../build/webpack/loaders/utils'
import { stringify } from 'querystring'
import { ParsedUrlQuery, stringify } from 'querystring'
import resolveRewrites from '../../shared/lib/router/utils/resolve-rewrites'

const wsServer = new ws.Server({ noServer: true })

Expand Down Expand Up @@ -77,8 +78,27 @@ export async function renderScriptError(
res.end('500 - Internal Error')
}

function addCorsSupport(req: IncomingMessage, res: ServerResponse) {
const isApiRoute = req.url!.match(API_ROUTE)
function isApiRouteAfterRewrites(
parsedUrl: UrlObject,
rewrites: HotReloader['rewrites']
): boolean {
const rewrittenRoute = resolveRewrites(
parsedUrl.pathname as string,
[],
rewrites,
parsedUrl.query as ParsedUrlQuery,
(s) => s
).resolvedHref
if (!rewrittenRoute)
return Boolean((parsedUrl.pathname || '').match(API_ROUTE))
return Boolean(rewrittenRoute.match(API_ROUTE))
}

function addCorsSupport(
req: IncomingMessage,
res: ServerResponse,
isApiRoute: boolean
) {
// API routes handle their own CORS headers
if (isApiRoute) {
return { preflight: false }
Expand Down Expand Up @@ -234,7 +254,11 @@ export default class HotReloader {
// With when the app runs for multi-zones support behind a proxy,
// the current page is trying to access this URL via assetPrefix.
// That's when the CORS support is needed.
const { preflight } = addCorsSupport(req, res)
const { preflight } = addCorsSupport(
req,
res,
isApiRouteAfterRewrites(parsedUrl, this.rewrites)
)
if (preflight) {
return {}
}
Expand Down
45 changes: 45 additions & 0 deletions test/development/api-cors-with-rewrite/index.test.ts
@@ -0,0 +1,45 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { fetchViaHTTP } from 'next-test-utils'

describe('Rewritten API Requests should pass OPTIONS requests to the api function', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/api/some-endpoint.js': `
export default (req, res) => {
res.end("successfully hit some-endpoint!")
}
`,
},
nextConfig: {
rewrites: () =>
Promise.resolve({
beforeFiles: [
// Nextjs by default requires a /api prefix, let's remove that
{
source: '/:path*',
destination: '/api/:path*',
},
],
afterFiles: [],
fallback: [],
}),
},
dependencies: {},
})
})
afterAll(() => next.destroy())

it('should pass OPTIONS requests to the api function', async () => {
const res = await fetchViaHTTP(next.url, '/some-endpoint', null, {
method: 'OPTIONS',
headers: {
Origin: 'http://localhost:3000',
},
})
expect(await res.text()).toContain('successfully hit some-endpoint!')
})
})