serverless-express icon indicating copy to clipboard operation
serverless-express copied to clipboard

integration: lambda gives "Unable to determine event source based on event."

Open PeterTrotter opened this issue 4 years ago • 23 comments

Hi all,

When deploying using serverless with serverless-api-gateway-caching like so:

functions:
  graphql:
    handler: src/handler.graphqlHandler
    events:
    - http:
        path: graphql
        method: post
        cors: true
        integration: lambda
        caching:
          enabled: true
          cacheKeyParameters:
            - name: integration.request.header.bodyValue
              mappedFrom: method.request.body

The event is structured like this:

{
  body: {
    ...gql...
  },
  method: 'POST',
  principalId: '',
  stage: 'dev',
  cognitoPoolClaims: { sub: '' },
  enhancedAuthContext: {},
  headers: {
    Accept: '*/*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'en-GB,en;q=0.5',
    'cache-control': 'no-cache',
    'CloudFront-Forwarded-Proto': 'https',
    'CloudFront-Is-Desktop-Viewer': 'true',
    'CloudFront-Is-Mobile-Viewer': 'false',
    'CloudFront-Is-SmartTV-Viewer': 'false',
    'CloudFront-Is-Tablet-Viewer': 'false',
    'CloudFront-Viewer-Country': 'GB',
    'content-type': 'application/json',
    dnt: '1',
    Host: '...host url...',
    origin: 'https://...origin url...',
    pragma: 'no-cache',
    Referer: 'https://...referrer url...',
    'sec-gpc': '1',
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0',
    Via: '2.0 xxxxxx.cloudfront.net (CloudFront)',
    'X-Amz-Cf-Id': 'xxxxxx',
    'X-Amzn-Trace-Id': 'Root=xxxxxx',
    'X-Forwarded-For': '...some ips...',
    'X-Forwarded-Port': '443',
    'X-Forwarded-Proto': 'https'
  },
  query: {},
  path: {},
  identity: {
    cognitoIdentityPoolId: '',
    accountId: '',
    cognitoIdentityId: '',
    caller: '',
    sourceIp: '...ip...',
    principalOrgId: '',
    accessKey: '',
    cognitoAuthenticationType: '',
    cognitoAuthenticationProvider: '',
    userArn: '',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0',
    user: ''
  },
  stageVariables: {},
  requestPath: '/graphql'
}

It seems you cannot enable method.request.body cacheKeyParameters without integration: lambda which seems to mess with the event.

Should this be an event type we are able to handle or am I missing a setting to turn it in to an event that can be handled?

PeterTrotter avatar May 25 '21 12:05 PeterTrotter

Looks like event.requestContext is missing from that https://github.com/vendia/serverless-express/blob/mainline/src/event-sources/utils.js#L75. If you remove caching does this work? Is this an API Gateway V1 or V2 setup?

brettstack avatar May 31 '21 06:05 brettstack

This is an API Gateway V2 setup.

I'm certain that the caching is a red herring here and the issue is the type integration.

Lambda vs Lambda Proxy

https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-with-lambda-integration.html

As I understand it if you wish to allow caching for GraphQL (i.e. based on the request body) then you have to use Lambda integration instead of Lambda Proxy.

Switching to integration: lambda-proxy (obviously caching needs to remain disabled) works just fine.

Is the Lambda integration type beyond the scope of this project?

PeterTrotter avatar Jun 07 '21 11:06 PeterTrotter

Sorry for wasting your time - looks like the correct way to use this for my setup (apollo-server) is to use GET requests for queries which bypasses the need to use the body as a cache key. Which means there is no need to change from Lambda Proxy to Lambda.

https://www.apollographql.com/docs/apollo-server/requests/

PeterTrotter avatar Jun 07 '21 12:06 PeterTrotter

This should be reopened. @PeterTrotter may have had luck using integration: lambda-proxy, but others do need to use integration: lambda and this issue persists

dmattia avatar Jul 01 '21 15:07 dmattia

Happy to leave this open and review any PRs adding non-proxy support.

brettstack avatar Jul 02 '21 03:07 brettstack

We recently ran into this issue. Make sure your api gateway integration request is setup with a template mapping to set the requestContext

codyseibert avatar Nov 10 '21 19:11 codyseibert

https://github.com/vendia/serverless-express/issues/427#issuecomment-924580007

bumi avatar Nov 28 '21 07:11 bumi

I still cant get it going. I tried the following combinations using "@vendia/serverless-express" and "lambda-tester" with nodejs, express and internal routing for an AWS Serverless Lambda with API Gateway with SAM.

app.js ` const express = require('express'); const cors = require('cors'); const compression = require('compression');

const app = new express(); app.use(express.json({ limit: '6mb', extended: true })); app.use(express.urlencoded({ limit: '6mb', extended: true })); app.use(cors()); app.use(compression());

// Testing endpoint app.get('/', (req, res) => { res.send('Hello World'); });

//Must be included AWS SAM module.exports = app; `

lamba.js const serverlessExpress = require('@vendia/serverless-express'); const app = require('./app'); exports.handler = serverlessExpress({app});

test-handler.js ` 'use strict'; const myLambda = require('../../lambda'); const LambdaTester = require('lambda-tester'); const chai = require('chai'); const expect = chai.expect;

//First try describe('Tests index', function () { it('verifies successful response', async function () { return await LambdaTester(myLambda.handler) .event() .expectResult((result) => { expect(result.message).to.be.equal("Hello World"); }); }); });

//Second try describe('Tests index', function () { it('verifies successful response', function () { return LambdaTester(myLambda.handler) .event() .expectResult((result) => { expect(result.message).to.be.equal("hello world"); }); }); });

//Third try describe('Tests index', function () { it('verifies successful response', function () { LambdaTester(myLambda.handler) .event() .expectResult((result) => { expect(result.message).to.be.equal("hello world"); }); }); }); `

And everytime i get the same error - Error: Unable to determine event source based on event.

Anyone knows what to do?

gaestradaaedo avatar Dec 07 '21 03:12 gaestradaaedo

Getting the same error when using @vendia/serverless-express via apollo-server-lambda on Vercel.

[POST] /api
19:28:08:34
2022-01-09T18:28:10.719Z	2014d7ba-295d-4344-a94a-007608d4788b	ERROR	Invoke Error 	{"errorType":"Error","errorMessage":"Unable to determine event source based on event.","stack":["Error: Unable to determine event source based on event.","    at getEventSourceNameBasedOnEvent (/var/task/node_modules/@vendia/serverless-express/src/event-sources/utils.js:88:9)","    at proxy (/var/task/node_modules/@vendia/serverless-express/src/configure.js:38:51)","    at handler (/var/task/node_modules/@vendia/serverless-express/src/configure.js:99:12)","    at Object.handler (/var/task/node_modules/apollo-server-lambda/dist/ApolloServer.js:51:27)"]}

electerious avatar Jan 16 '22 11:01 electerious

I had this problem with serverless-plugin-warmup, and i need to set my payload event like this:

{
  "body": null,
  "httpMethod": "GET",
  "path": "/my-path",
  "requestContext": {}
}

--> Now, it's all working well :)

sebastien-abbal avatar Feb 28 '22 10:02 sebastien-abbal

@sebastien-abbal Issue here was the same: missing a requestContext

pbassut avatar May 01 '22 15:05 pbassut

We had the same problem.

Event:

events:
  - http:
      path: /webhooks/logs/export
      method: POST
      async: true

Problem

{
    "statusCode": 400,
    "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Credentials": true,
        "Date": "Wed, 29 Mar 2023 18:31:06 GMT"
    },
    "body": "{\"data\":null,\"errors\":[\"Unable to determine event source based on event.\"]}"
}

Resolution

const handler = serverlessExpress({ app })

function overrideAsyncEventToExpress(event: any) {
  event.path = '/export'
  event.requestContext = { identity: event?.identity }
  event.httpMethod = event.method
  event.queryStringParameters = Object.assign(event.query, (event?.queryStringParameters || {}))
  event.pathParameters = { proxy: '' }
  event.isBase64Encoded = event?.isBase64Encoded || false
  event.body = typeof event.body != 'string' ? JSON.stringify(event.body) : event.body
  event.multiValueHeaders = event.headers

  return event
}

export async function main(
  event: APIGatewayEvent,
  context: Context,
): Promise<APIGatewayProxyResult> {
  logger.debug('Event', event)

  let eventPath = event?.requestPath || event?.resource || null

  try {
    if (eventPath === '/webhooks/logs/export') {
      overrideAsyncEventToExpress(event)
    } else if (eventPath === '/webhooks/logs') {
      event.path = '/'
    }

    return handler(event, context)
  } catch (error) {
    logger.error(error)

    const statusCode = error.statusCode || 400
    return Response.Fail({ statusCode, body: '', errors: [error?.message] })
  }
}

Basically we need to factory some methods to event object when received Async Event from API Gateway

event.requestContext = { identity: event?.identity }
event.httpMethod = event.method
event.queryStringParameters = Object.assign(event.query, (event?.queryStringParameters || {}))
event.pathParameters = { proxy: '' }
event.isBase64Encoded = event?.isBase64Encoded || false
event.body = typeof event.body != 'string' ? JSON.stringify(event.body) : event.body
event.multiValueHeaders = event.headers

This is awful, because we need to manipulate event, so each change will be analyze because will could be a problem for us.

thadeu avatar Mar 29 '23 19:03 thadeu

Not using this package due to ^ this

Why can't we add a default event type and then custom overrides?

0xPT avatar Dec 14 '23 04:12 0xPT

Hey @0xPT , does this help? https://github.com/CodeGenieApp/serverless-express?tab=readme-ov-file#eventsource

brettstack avatar Dec 14 '23 07:12 brettstack

@brettstack unfortunately not, the documentation (and example) isn't clear enough, and this issue is pointing out the problem with having to redeclare every request/response properly.

My use case was Cognito triggers, and even trying to utilize the eventSource prop I 1) faced the error this issue brings up and 2) felt uncomfortable retyping the expected request/response object

0xPT avatar Dec 14 '23 07:12 0xPT

Which trigger specifically are you trying to use? I can add native support.

brettstack avatar Dec 14 '23 08:12 brettstack