Fix serverless loader for API routes (#7767)

* Fix serverless loader for API routes

* Only use params with dynamic routes
This commit is contained in:
Lukáš Huvar 2019-07-07 23:45:40 +02:00 committed by Joe Haddad
parent e995c73b2c
commit f204935251
5 changed files with 102 additions and 57 deletions

View file

@ -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

View file

@ -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
)
}
/**

View file

@ -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 (

View file

@ -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

View file

@ -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')