Fix serverless loader for API routes (#7767)
* Fix serverless loader for API routes * Only use params with dynamic routes
This commit is contained in:
parent
e995c73b2c
commit
f204935251
5 changed files with 102 additions and 57 deletions
|
@ -4,10 +4,56 @@ import { Stream } from 'stream'
|
|||
import getRawBody from 'raw-body'
|
||||
import { parse } from 'content-type'
|
||||
import { Params } from './router'
|
||||
import { PageConfig } from '../types'
|
||||
import { interopDefault } from './load-components'
|
||||
|
||||
export type NextApiRequestCookies = { [key: string]: string }
|
||||
export type NextApiRequestQuery = { [key: string]: string | string[] }
|
||||
|
||||
export async function apiResolver(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
params: any,
|
||||
resolverModule: any
|
||||
) {
|
||||
try {
|
||||
let bodyParser = true
|
||||
if (!resolverModule) {
|
||||
res.statusCode = 404
|
||||
res.end('Not Found')
|
||||
return
|
||||
}
|
||||
|
||||
if (resolverModule.config) {
|
||||
const config: PageConfig = resolverModule.config
|
||||
if (config.api && config.api.bodyParser === false) {
|
||||
bodyParser = false
|
||||
}
|
||||
}
|
||||
// Parsing of cookies
|
||||
setLazyProp({ req }, 'cookies', getCookieParser(req))
|
||||
// Parsing query string
|
||||
setLazyProp({ req, params }, 'query', getQueryParser(req))
|
||||
// // Parsing of body
|
||||
if (bodyParser) {
|
||||
req.body = await parseBody(req)
|
||||
}
|
||||
|
||||
res.status = statusCode => sendStatusCode(res, statusCode)
|
||||
res.send = data => sendData(res, data)
|
||||
res.json = data => sendJson(res, data)
|
||||
|
||||
const resolver = interopDefault(resolverModule)
|
||||
resolver(req, res)
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
sendError(res, e.statusCode, e.message)
|
||||
} else {
|
||||
sendError(res, 500, e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming message like `json` or `urlencoded`
|
||||
* @param req request object
|
||||
|
|
|
@ -23,31 +23,16 @@ import {
|
|||
} from '../lib/router/utils'
|
||||
import * as envConfig from '../lib/runtime-config'
|
||||
import { NextApiRequest, NextApiResponse } from '../lib/utils'
|
||||
import {
|
||||
getQueryParser,
|
||||
sendJson,
|
||||
sendData,
|
||||
parseBody,
|
||||
sendError,
|
||||
ApiError,
|
||||
sendStatusCode,
|
||||
setLazyProp,
|
||||
getCookieParser,
|
||||
} from './api-utils'
|
||||
import { apiResolver } from './api-utils'
|
||||
import loadConfig from './config'
|
||||
import { recursiveReadDirSync } from './lib/recursive-readdir-sync'
|
||||
import {
|
||||
interopDefault,
|
||||
loadComponents,
|
||||
LoadComponentsReturnType,
|
||||
} from './load-components'
|
||||
import { loadComponents, LoadComponentsReturnType } from './load-components'
|
||||
import { renderToHTML } from './render'
|
||||
import { getPagePath } from './require'
|
||||
import Router, { route, Route, RouteMatch, Params } from './router'
|
||||
import { sendHTML } from './send-html'
|
||||
import { serveStatic } from './serve-static'
|
||||
import { isBlockedPage, isInternalUrl } from './utils'
|
||||
import { PageConfig } from 'next-server/types'
|
||||
|
||||
type NextConfig = any
|
||||
|
||||
|
@ -289,7 +274,6 @@ export default class Server {
|
|||
res: NextApiResponse,
|
||||
pathname: string
|
||||
) {
|
||||
let bodyParser = true
|
||||
let params: Params | boolean = false
|
||||
|
||||
let resolverFunction = await this.resolveApiRequest(pathname)
|
||||
|
@ -307,43 +291,12 @@ export default class Server {
|
|||
}
|
||||
}
|
||||
|
||||
if (!resolverFunction) {
|
||||
res.statusCode = 404
|
||||
res.end('Not Found')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const resolverModule = require(resolverFunction)
|
||||
|
||||
if (resolverModule.config) {
|
||||
const config: PageConfig = resolverModule.config
|
||||
if (config.api && config.api.bodyParser === false) {
|
||||
bodyParser = false
|
||||
}
|
||||
}
|
||||
// Parsing of cookies
|
||||
setLazyProp({ req }, 'cookies', getCookieParser(req))
|
||||
// Parsing query string
|
||||
setLazyProp({ req, params }, 'query', getQueryParser(req))
|
||||
// // Parsing of body
|
||||
if (bodyParser) {
|
||||
req.body = await parseBody(req)
|
||||
}
|
||||
|
||||
res.status = statusCode => sendStatusCode(res, statusCode)
|
||||
res.send = data => sendData(res, data)
|
||||
res.json = data => sendJson(res, data)
|
||||
|
||||
const resolver = interopDefault(resolverModule)
|
||||
resolver(req, res)
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
sendError(res, e.statusCode, e.message)
|
||||
} else {
|
||||
sendError(res, 500, e.message)
|
||||
}
|
||||
}
|
||||
apiResolver(
|
||||
req,
|
||||
res,
|
||||
params,
|
||||
resolverFunction ? require(resolverFunction) : undefined
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,7 +73,14 @@ export function createEntrypoints(
|
|||
const bundlePath = join('static', buildId, 'pages', bundleFile)
|
||||
|
||||
if (isApiRoute && target === 'serverless') {
|
||||
server[join('pages', bundleFile)] = [absolutePagePath]
|
||||
const serverlessLoaderOptions: ServerlessLoaderQuery = {
|
||||
page,
|
||||
absolutePagePath,
|
||||
...defaultServerlessOptions,
|
||||
}
|
||||
server[join('pages', bundleFile)] = `next-serverless-loader?${stringify(
|
||||
serverlessLoaderOptions
|
||||
)}!`
|
||||
} else if (isApiRoute || target === 'server') {
|
||||
server[bundlePath] = [absolutePagePath]
|
||||
} else if (
|
||||
|
|
|
@ -38,7 +38,31 @@ const nextServerlessLoader: loader.Loader = function() {
|
|||
/\\/g,
|
||||
'/'
|
||||
)
|
||||
return `
|
||||
|
||||
if (page.startsWith('/api')) {
|
||||
return `
|
||||
${
|
||||
isDynamicRoute(page)
|
||||
? `
|
||||
import { getRouteMatcher } from 'next-server/dist/lib/router/utils/route-matcher';
|
||||
import { getRouteRegex } from 'next-server/dist/lib/router/utils/route-regex';
|
||||
`
|
||||
: ``
|
||||
}
|
||||
import { apiResolver } from 'next-server/dist/server/api-utils'
|
||||
|
||||
export default (req, res) => {
|
||||
const params = ${
|
||||
isDynamicRoute(page)
|
||||
? `getRouteMatcher(getRouteRegex('${page}'))(req.url)`
|
||||
: `{}`
|
||||
}
|
||||
const resolver = require('${absolutePagePath}')
|
||||
apiResolver(req, res, params, resolver)
|
||||
}
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
import {parse} from 'url'
|
||||
import {renderToHTML} from 'next-server/dist/server/render';
|
||||
import {sendHTML} from 'next-server/dist/server/send-html';
|
||||
|
@ -120,6 +144,7 @@ const nextServerlessLoader: loader.Loader = function() {
|
|||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export default nextServerlessLoader
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
File
|
||||
} from 'next-test-utils'
|
||||
import json from '../big.json'
|
||||
import { createServer } from 'http'
|
||||
|
||||
const appDir = join(__dirname, '../')
|
||||
let appPort
|
||||
|
@ -207,6 +208,19 @@ function runTests (serverless = false) {
|
|||
)
|
||||
)
|
||||
expect(Object.keys(pagesManifest).includes('/api/[post]')).toBeTruthy()
|
||||
|
||||
const port = await findPort()
|
||||
const resolver = require(join(
|
||||
appDir,
|
||||
'.next/serverless/pages/api/blog.js'
|
||||
)).default
|
||||
|
||||
const server = createServer(resolver).listen(port)
|
||||
const res = await fetchViaHTTP(port, '/api/nextjs')
|
||||
const json = await res.json()
|
||||
server.close()
|
||||
|
||||
expect(json).toEqual([{ title: 'Cool Post!' }])
|
||||
} else {
|
||||
expect(
|
||||
existsSync(join(appDir, '.next/server/pages-manifest.json'), 'utf8')
|
||||
|
|
Loading…
Reference in a new issue