I have created a simple lambda function having following code.
exports.handler = (event, context, callback) => {
const operation = event.body.operation;
console.log("operation = ", operation)
switch (operation) {
case 'add': callback(null, 'post method');
break;
case 'add1': callback(null, {
status: 0,
errorType: "InternalServerError",
errorCode: "001",
errorMessage: "post method error."
}
);
default: callback(null, 'Hello from Lambda');
break;
}
};
It will be connected with Amazon API Gateway. Using a REST client able to get success & error responses. But HTTP status code is still 200. Then I have modified API Gateway integration responses in two ways.
1. Selection pattern : “InternalServerError”
2. Selection pattern : “.*InternalServerError”
Method response : 500
But I still got 200 HTTP status code. What is the actual issue related with this selection patterns?
API Gateway checks for the error pattern when the error is thrown from Lambda function using context.fail(). Refer to this article for more details on handling Lambda error in API GW.
In your case you need to return a proper HTTP response, from my answer here:
Related
I'd really like to be able to pass custom errors back out of my Lambda function via the API Gateway.
I'm generating the errors this way:
if (error) { // failure
APIGatewayResult = { // set error object
statusCode: 608,
message: 'File upload to buffer failed.',
error: error
};
done();
};
I'm fairly certain the format above is either parsed incorrectly or I can't pass it back out like this:
done = (err, res) => callback(null, {
statusCode: err ? APIGatewayResult.statusCode : APIGatewayResult.statusCode,
body: err ? JSON.stringify(APIGatewayResult) : JSON.stringify(APIGatewayResult),
headers: {
'Content-Type': 'application/json',
},
});
I'd like the response I get from API gateway to look like this:
{
"statusCode": 608,
"message": "File upload to buffer failed.",
"error": {}
}
Instead of this:
{
"message": "Internal server error"
}
There are two parts to your question:
1) How to handle Lambda error messages in API Gateway:
There are numerous guides available which explain how to parse Lambda error responses in API Gateway to return them in the desired format to consumers. For example: Error Handling Patterns in Amazon API Gateway and AWS Lambda.
Important to note that you're parsing null to the first parameter of the callback function from your lambda. The first parameter is the error response message, so by providing it as null the Lambda will be returning a successful 200 response back to API Gateway.
2) How to override the generic unhandled exception message in API Gateway:
This is required because as mentioned in the comments, the error you're receiving appears to have been thrown due to an unhandled exception in your application. You'll need to review the logs to identify the source of the issue.
But to change the format of the default error response object you'll need to add a custom Gateway response in API Gateway.
It's difficult to offer a more concrete example given the limited information provided but this should be enough to point you in the right direction to find what you're looking for.
I am trying to get some data via Webhook into lambda function using API Gateway and SQS. This Webhook contains a JSON payload, and HTTP headers that provide context. Since there is no way to get these webhook HTTP headers from API Gateway to SQS using "Action=SendMessage" in Mapping template (correct me if my assumption is wrong...I have tried http headers in method request and integration request), I have included the headers from webhook in the API Gateway mapping template as follows:
Action=SendMessage&MessageGroupId=$input.params('MessageGroupId')&
MessageBody={
"Header1":"$input.params('Header1 key')",
"body":"$input.json('$')"
}
My code in Lambda function is as follows:
'use strict';
exports.handler = async function(event, context) {
if (event.Records) {
event.Records.forEach(record => {
var rec = JSON.parse(JSON.stringify(record.body));
console.log("Record body: " + rec);
console.log("Header1: " + rec.Header1);
});
}
return {};
};
The value of 'rec' in CloudWatch log is rather quite long but here is the simplified version:
Record body: {
"Header1":" OYqmDnuUpDs8oF1RwoBnJywBY6c1I4qLklU=",
"body":"{"id":820982911946154508,"email":"jon#doe.ca","closed_at":null,"created_at":"2020-11-30T20:25:43-05:00","updated_at":"2020-11-30T20:25:43-05:00","number":234,"note":null,"token":"123456abcd","gateway":null,"test":true,"total_price":"7.00","subtotal_price":"-3.00"
}
My question - how can I read the values of Header1 and body? I have tried rec.Header1, rec[0].Header1 and rec["Header1"] etc. and I get "Undefined" in all the cases. Thanks.
The body is already stringified.
You need to simply call JSON.parse(record.body) to parse the string to valid JSON. Note that this could throw an error.
here is my code that makes an Http Get request to an API end point from one of the services running on Amazon Fargate service. The API is powered by Amazon API gateway and Lambda. Also this is a private api used with in the VPC and I also have setup the apigateway VPC end point to facilitate the same. I have received this error only once. All the subsequent calls made to the API were successful.
My suspicion is that the lambda was not warm and that resulted a timeout. I am going to try setting a timeout for the axios code. any suggestions welcome
async getItems(): Promise < any > {
try {
let url = `https://vpce-[id].execute-api.ap-southeast-2.vpce.amazonaws.com/prod/items`
const response = await axios.get(url, {
headers: {
'Authorization': `Bearer ${token}`,
'x-apigw-api-id': `[api-id]`
}
});
return response.data;
} catch(error) {
console.log(error);
throw error;
}
}
Turns out my lambda is timing out after the 30 seconds configured time. I could increase the lambda timeout, but the configurable timeout for API gateway is 30 seconds.
It has only happened once and i believe that it's because lambda cold start. As a workaround, I am taking the retry approach. The API request will be retried 3 times.
I'm trying to create a lambda authorizer on aws using node.js async/await instead of callbacks but there is no information on how to create the HTTP response returned to API Gateway. For example, if i return this :
{
statusCode: 401
}
the API gateway doesn't seem to understand and return an error 403 to the client :
{
"statusCode": 403,
"error": "Forbidden",
"message": "No principalId set on the Response"
}
Does anyone knows how to do what is described here : https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
but using async / await ?
Thanks in advance !
EDIT :
The way to return an error 401 is simply to throw an error like this :
throw new Error("Unauthorized")
And if the user is explicitly deny / allow, simply return the JSON policy.
To return a 401 error you simply need to throw an error with "Unauthorized" as message, like this :
throw new Error("Unauthorized")
And if the user is explicitly deny / allow, simply return the JSON policy like you would do with callbacks.
I think the accepted solution does not work (anymore). I tried it like this:
exports.authorize = async (event, context) => {
throw new Error("Unauthorized")
}
It works but in my logs I can see this error:
ERROR Invoke Error {"errorType":"Error","errorMessage":"Unauthorized","stack":["Error: Unauthorized"," at Runtime.exports.authorize [as handler] (/var/task/handler/auth.js:21:13)"," at processTicksAndRejections (internal/process/task_queues.js:97:5)"]}
From what I've read (some code samples would be helpful) it sounds like you're not calling the callback right or it's not called in the right place. You can use
callback("Some error message.");
to send back a response w/ a 401 status code. You can also change this by doing something like:
var response = {
statusCode: 401, /* some number */
body: "Oops!" /* some message */
};
callback(null, response);
I would check out this page for more information.
I have a serverless lambda function written in Node.JS.
What is the best / correct way of returning error codes?
The pattern that I use right now (and it works!) is:
module.exports.endpoint = (event, context, callback) => {
const response = {
statusCode: 404,
body: JSON.stringify({ message: 'Hello World!' })
};
callback(null, response);
}
When I make a call, for example from POSTMAN, to my endpoint I get:
Status: 404 Not Found which is exactly what I'm expecting.
Moreover, in the logs I can see:
Serverless: GET / (λ: get)
Serverless: [404] {"statusCode":404,"body":"{\"message\":\"Hello World!\"}"}
That works well.
What bothers me is that I'm passing null as the error. Looking into a few other tutorials/examples I've found patterns like:
https://aws.amazon.com/blogs/compute/error-handling-patterns-in-amazon-api-gateway-and-aws-lambda/
https://serverless.com/framework/docs/providers/aws/events/apigateway/
callback ("the sky is falling!");
callback("[BadRequest] Validation error: Missing field 'name'");
callback("[404] Not Found");
callback(new Error('[404] Not found'));
callback(JSON.stringify(myErrorObj));
All of them make perfect sense and you can specify HTTP Status Code - yet what I'm getting is HTTP Status Code 200 in the end. When I look at the logs I can see that the error was followed straight after with 200:
Serverless: GET / (λ: get)
Serverless: Failure: the sky is falling!
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [BadRequest] Validation error: Missing field 'name'
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [404] Not Found
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [404] Not found
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: {"errorType":"InternalServerError","httpStatus":500,"message":"An unknown error has occurred. Please try again."}
Serverless: Replying 200
In this place I've found following explanation:
https://github.com/serverless/serverless/issues/4119
If you want to respond with HTTP errors in that case, you have to
encode the HTTP error as successful Lambda response
with following example:
Sample 403:
callback(null, { statusCode: 403, body: "Forbidden", headers: { "Content-Type": "text/plain" } });
Sample 404:
callback(null, { statusCode: 400 });
So that's basically the same way I've. For the sake of completeness, I can add that there is also a plenty of examples that uses context.fail(result) or context.succeed(result) - but from what I gathered context is deprecated and shouldn't be used (even though it still works).
What is the point of using callback(error)?
If you want to respond with HTTP errors in that case, you have to encode the HTTP error as successful Lambda response
This type of error-handling is specific to API Gateway.
Just like in traditional Node web servers (e.g. express), you can throw any error using throw new Error('Invalid Payload') and the middleware usually converts it into an HTTP response with the correct response status.
In API Gateway Lambda, this can be written like this...
function createResponse(status, body) {
return {
headers: {
'Access-Control-Allow-Origin': '*'
},
statusCode: status,
body: JSON.stringify(body)
}
}
module.exports.endpoint = (event, context, callback) => {
try {
return callback(null, createResponse(200, processEvent(event)))
} except (e) {
console.error(e)
return callback(null, createResponse(500, {
error: 'Internal Server Error'
}))
}
}
Basically, it's a handled error. The lambda function succeeded but the request failed (maybe 400, 404, or 500).
You should be handling errors always, or else if your handler crashes (due to a runtime error or syntax error or any unhandled error), your user will get an unexpected response (a 500 or a 502) which you probably don't want.
Please remember that Lambda is not only used for API Gateway. callback(error) is used for non-API Gateway-triggered Lambdas.
For example, if you have an SNS-triggered Lambda, you can return callback('Any error message here') and it will let SNS know that it failed and so SNS can retry the invocation.