serverless-express
serverless-express copied to clipboard
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?
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?
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?
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/
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
Happy to leave this open and review any PRs adding non-proxy support.
We recently ran into this issue. Make sure your api gateway integration request is setup with a template mapping to set the requestContext
https://github.com/vendia/serverless-express/issues/427#issuecomment-924580007
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?
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)"]}
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 Issue here was the same: missing a requestContext
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.
Not using this package due to ^ this
Why can't we add a default event type and then custom overrides?
Hey @0xPT , does this help? https://github.com/CodeGenieApp/serverless-express?tab=readme-ov-file#eventsource
@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
Which trigger specifically are you trying to use? I can add native support.