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

Nuxt 3.0.0-rc.9 with Nitro 0.5.0 configuration error: Malformed Lambda proxy response #504

Closed
rhumie opened this issue Sep 15, 2022 · 11 comments · Fixed by #834
Closed

Nuxt 3.0.0-rc.9 with Nitro 0.5.0 configuration error: Malformed Lambda proxy response #504

rhumie opened this issue Sep 15, 2022 · 11 comments · Fixed by #834
Labels
bug Something isn't working

Comments

@rhumie
Copy link

rhumie commented Sep 15, 2022

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 (#357 (comment)), 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.

Logs

No response

@rhumie
Copy link
Author

rhumie commented Sep 15, 2022

@danielroe
At this point, is there any other way than to create a wrapper for the handler (index.mjs) ourselves?

Copy link
Member

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!)

@rhumie
Copy link
Author

rhumie commented Sep 15, 2022

Thank you for your prompt response. I will try the approach you suggested.

@brtinney
Copy link

brtinney commented Sep 15, 2022

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

@timmak
Copy link

timmak commented Oct 16, 2022

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!)

This solution doesn't seem to work with current version of nuxt, any suggestion on how to do this?

@rhumie
Copy link
Author

rhumie commented Oct 21, 2022

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

@timmak
Copy link

timmak commented Oct 22, 2022

@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

@danielroe danielroe added the bug Something isn't working label Oct 22, 2022 — with Volta.net
@PizzaConsole
Copy link

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.

@boogiefromzk
Copy link

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

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,
    },

@boogiefromzk
Copy link

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!]))
}

@boogiefromzk
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants