Reputation: 1248
I'm using an API-Gateway + Lambda combo to process a POST request. I'm using Node + Serverless Framework.
When I run serverless offline, I am able to send a POST request and have my data stored on an S3. But when I deploy it and run the same POST request, I get a "502 Internal Server Error" message. Because it works locally but not in production, I'm pretty sure I have some permission/config problems.
saveToS3(newData)
.then(result => {
callback(null, {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: "Successfully added data!"
});
})
.catch(e => callback(null, { statusCode: 500, body: JSON.stringify(e) }));
What I've checked:
What I haven't checked:
serverless.yml
file?My yml:
service: myService
provider:
name: aws
runtime: nodejs12.x
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:GetObject"
- "s3:PutObject"
Resource: "arn:aws:s3:::myS3Bucket/*"
functions:
saveToS3:
handler: handler.saveToS3
events:
- http:
path: users
method: post
cors: true
plugins:
- serverless-offline
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: myS3Bucket
Upvotes: 2
Views: 9778
Reputation: 81
I'm using CDK. In my case I added the parameter binaryMediaTypes: ["application/json"]
, which is used incorrectly:
const myApi = new ApiGw.RestApi(this, "my-api", {
restApiName: `my-awesome-api`,
defaultCorsPreflightOptions: {
allowOrigins: ApiGw.Cors.ALL_ORIGINS,
allowMethods: ApiGw.Cors.ALL_METHODS,
allowHeaders: ["Content-Type", "Accept", ...],
},
...
binaryMediaTypes: ["application/json"], // <-- Problematic property
});
binaryMediaTypes
is useful when you create an API that will serve as a proxy of a specific AWS
service (e.g. S3
), and you will send binary data, like images, documents, etc.
Removing binaryMediaTypes
from the API definition, it worked like a charm
Upvotes: 0
Reputation: 403
In our case, we are using serverless + API Gateway + Lambda. Our main app.ts
exports an async handler wrapped by serverless. Ex (EnvironmentService
is just a service layer for the environment config):
const allowedBinaryMimeTypes = EnvironmentService.getFileMimeTypesAllowed();
...
const handler = serverless(app, { binary: allowedBinaryMimeTypes });
Before modifying any code, the 502 Bad Gateway Error log was showing this (AWS CloudWatch):
Our solution was to override the default aws provider timeout in serverless.yml:
S3Service file (call to getObject):
static async getObject(type: string, pathParams: APIGatewayProxyEventPathParameters): Promise<any> {
const params = await this.setRequestParams(type, pathParams.id, pathParams.fileName);
try {
// get S3 object/file
const data = await S3.getObject(params).promise();
// some bug with AWS converting JPG objects to JPEGg
const contentType = data.ContentType === 'image/jpeg' ? 'image/jpg' : data.ContentType;
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Methods': 'OPTIONS,GET',
'Content-Type': contentType
},
body: data.Body.toString('base64'),
isBase64Encoded: true
};
} catch (err) {
console.error(err);
throw err;
}
}
Upvotes: 0
Reputation: 1248
Found the issue, facepalming because it took me hours to find it.
I had two problems:
My main lambda function had an "async" in front of it, but I was implementing it with callbacks. Removing the "async" fixed it.
My response format was missing the "headers" and "isBase64Encoded" fields. Including that removed the 502 error (see below).
Helpful links: - https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format - (if you're using serverless framework) https://github.com/dherault/serverless-offline/issues/405
If using API Gateway, make sure your lambda function's response looks like the code snippet below. Otherwise it will throw a "502 - Malformed Lambda Function" error.
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}
Upvotes: 4
Reputation:
If you are using proxy integration you need to be careful because for every possible syntax error it will throw an internal server error.
catch(e => callback(null, { statusCode: 500, body: JSON.stringify(e) }));
Can the error be not setting the headers here? If not it might probably be a syntax error.
Upvotes: 1