nitro
nitro copied to clipboard
Nuxt 3.0.0-rc.9 with Nitro 0.5.0 configuration error: Malformed Lambda proxy response
Environment
Nuxt 3.0.0-rc.9 with Nitro 0.5.0
Reproduction
I build the Nuxt app with aws-lambda deployment setting and configured Amazon API Gateway proxy integration to work with this lambda function. When I call my endpoint, I receive a configuration error and an HTTP 502 status code.
Execution failed due to configuration error: Malformed Lambda proxy response
Describe the bug
As I commented in the PR (https://github.com/unjs/nitro/pull/357#issuecomment-1247562089), this is due to the fact that cookies are returned in the response object.
For API Gateway to handle a Lambda function's response, the function must return output according to the following JSON format. Additional fields are not allowed.
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
cf. https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/?nc1=h_ls
Additional context
Related Issues and PRs are as follows.
- https://github.com/unjs/nitro/pull/357
- https://github.com/unjs/nitro/issues/245
Logs
No response
@danielroe At this point, is there any other way than to create a wrapper for the handler (index.mjs) ourselves?
Yes, Nitro presets are fully extensible. Copy this file into your project, make the necessary changes, and set nitro.entry to point to your changed file...
(Of course, we do plan to fix the issue in Nitro itself!)
Thank you for your prompt response. I will try the approach you suggested.
@datake914 (and others affected): sorry about the push for the change in #357 -- the separate cookies property is required if you are using Cloudfront vs. API Gateway.
Yes, Nitro presets are fully extensible. Copy this file into your project, make the necessary changes, and set
nitro.entryto point to your changed file...(Of course, we do plan to fix the issue in Nitro itself!)
This solution doesn't seem to work with current version of nuxt, any suggestion on how to do this?
@timmak I am using Nuxt 3.0.0-rc.11 with Nitro 0.5.4 and it works fine.
I specify in nuxt.config.ts to use custom nitro.entry.ts only when process.env.NODE_ENV is production.
nitro: {
preset: 'aws-lambda',
serveStatic: false,
entry: process.env.NODE_ENV === 'production' ? 'nitro.entry.ts' : undefined,
},
@datake914 Maybe what I am missing is where to import nitroApp from in nitro.entry.ts, as currently it not outputting similar output as without the entry file
make the necessary changes
@danielroe What are those changes? I am trying to do this with the cloudflare-pages.ts one and I can't get it to work.
@timmak I am using Nuxt 3.0.0-rc.11 with Nitro 0.5.4 and it works fine.
I specify in
nuxt.config.tsto use customnitro.entry.tsonly whenprocess.env.NODE_ENVis production.nitro: { preset: 'aws-lambda', serveStatic: false, entry: process.env.NODE_ENV === 'production' ? 'nitro.entry.ts' : undefined, },
The path to nitro.entry.ts should be absolute, so for me this worked (considering nitro.entry.ts is placed in the root folder of the project):
nitro: {
preset: 'aws-lambda',
serveStatic: false,
entry: process.env.NODE_ENV === 'production' ? path.join(__dirname, 'nitro.entry.ts') : undefined,
},
make the necessary changes
@danielroe What are those changes? I am trying to do this with the cloudflare-pages.ts one and I can't get it to work.
I had to fix path to the nitro app and comment-out cookies in the response:
import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, APIGatewayProxyEventV2, APIGatewayProxyResult, APIGatewayProxyResultV2, Context } from 'aws-lambda'
import '#internal/nitro/virtual/polyfill'
import { withQuery } from 'ufo'
import { nitroApp } from './node_modules/nitropack/dist/runtime/app'
// Compatibility types that work with AWS v1, AWS v2 & Netlify
type Event = Omit<APIGatewayProxyEvent, 'pathParameters' | 'stageVariables' | 'requestContext' | 'resource'> | Omit<APIGatewayProxyEventV2, 'pathParameters' | 'stageVariables' | 'requestContext' | 'resource'>
type Result = Exclude<APIGatewayProxyResult | APIGatewayProxyResultV2, string> & { statusCode: number }
export const handler = async function handler (event: Event, context: Context): Promise<Result> {
const query = { ...event.queryStringParameters, ...(event as APIGatewayProxyEvent).multiValueQueryStringParameters }
const url = withQuery((event as APIGatewayProxyEvent).path || (event as APIGatewayProxyEventV2).rawPath, query)
const method = (event as APIGatewayProxyEvent).httpMethod || (event as APIGatewayProxyEventV2).requestContext?.http?.method || 'get'
if ('cookies' in event && event.cookies) {
event.headers.cookie = event.cookies.join(';')
}
const r = await nitroApp.localCall({
event,
url,
context,
headers: normalizeIncomingHeaders(event.headers),
method,
query,
body: event.body // TODO: handle event.isBase64Encoded
})
const outgoingCookies = r.headers['set-cookie']
const cookies = Array.isArray(outgoingCookies) ? outgoingCookies : outgoingCookies?.split(',') || []
return {
// cookies,
statusCode: r.status,
headers: normalizeOutgoingHeaders(r.headers),
body: r.body.toString()
}
}
function normalizeIncomingHeaders (headers?: APIGatewayProxyEventHeaders) {
return Object.fromEntries(Object.entries(headers || {}).map(([key, value]) => [key.toLowerCase(), value!]))
}
function normalizeOutgoingHeaders (headers: Record<string, string | string[] | undefined>) {
return Object.fromEntries(Object.entries(headers)
.filter(([key]) => !['set-cookie'].includes(key))
.map(([k, v]) => [k, Array.isArray(v) ? v.join(',') : v!]))
}
@danielroe I've managed to make AWS Lambda work, the instructions are above, the simplest solution for this issue is adding another aws-lambda-no-cookies preset to nitro.