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

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?

About this issue

Most upvoted comments

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 πŸ˜ƒ

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

What trigger/event source are you using

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.

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

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?