Passing custom errors back to AWS API Gateway from AWS Lambda using node.js - node.js

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.

Related

axios.post() error about circular structure

Please guide me in case I'm not in proper use of axios. This simple piece of code can directly run:
const axios = require('axios')
axios.post('https://exp.host/--/api/v2/push/send', {"to":["ExponentPushToken[xxxxxxx]"],"title":"test title","body":"test body."})
.then(responseExpo => {
console.log("expo replied normally: " + JSON.stringify(responseExpo));
})
.catch(error => {
console.log("expo replied with error: " + JSON.stringify(error,null,4));
});
The result is:
Promise { <pending> }
expo replied with error: {}
"axios": "^0.19.2"
I tried to post with api tools and see a response with normal 200 status code:
{
"data":[
{
"status": "error",
"message": "\"ExponentPushToken[xxxxxxx]\" is not a registered push notification recipient",
"details":{
"error": "DeviceNotRegistered"
}
}
]
}
(you may ignore the "error": "DeviceNotRegistered" inside this json cos it's expected because I have put an invalid xxxxx input value when calling the api. Even putting a valid input value the result is still returning to the catch block with empty error)
I'm expecting it to return to the then block cos the server actually response with 200 with well formatted json result.
Have I done something wrong so that the call returns to the catch block? Cos the error is empty I have no idea what went wrong.
===============================
after jfriend's reminder I changed to directly disply the error.
console.log("expo replied with error: " + error);
it is show like this now:
Promise { <pending> }
expo replied with error: TypeError: Converting circular structure to JSON
--> starting at object with constructor 'ClientRequest'
| property 'socket' -> object with constructor 'TLSSocket'
--- property '_httpMessage' closes the circle
Anyone can let me know what exactly it means and guide me how to correct my usage?
(problem resolved). the response (responseExpo in the question) is neither a plain data JSON nor a plain string. it is an object with (see github.com/axios/axios#response-schema) some attributes. The real response content is inside "response.data". I was wrongly treating the response to be a plain json object or the http response content.
I had a similar problem and as solution, I used HttpService from nestjs which returns Observable<AxiosResponse<T>>. I fixed the problem by piping and plucking the request like this:
http.put<T>(url, data, config).pipe(pluck('data'))
I had a similar problem with HttpService from nestjs which returns Observable<AxiosResponse<any>>. I resolve with:
this.httpService.post(this.legacyAccessTokenEndpoint, form, { headers: form.getHeaders() }).pipe(map(x => x?.data))

Lambda authorizer response using async/await in Node.js

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.

Property `message` in error response body cannot be accessed | Hyperledger Fabric

Description
Using node I make a typical API POST request for which I have a .catch block with response variable say err. On error, the response body is returned as such (as seen from the Hyperledger Composer REST app)
{
"error": {
"statusCode": 500,
"name": "Error",
"message": "error trying invoke chaincode. Error: chaincode error (status: 500, message: Error: Payment needs to be of positive value)",
"stack": "Error: error trying invoke chaincode. Error: chaincode error (status: 500, message: Error: Payment needs to be of positive value)\n at _initializeChannel.then.then.then.then.catch (/home/ubuntu/.nvm/versions/node/v6.11.1/lib/node_modules/composer-rest-server/node_modules/composer-connector-hlfv1/lib/hlfconnection.js:839:34)"
}
}
My Issue
I am simply trying to fetch the message property from the above response and send it to the UI. But weirdly err.message does not give me the value I see inside message but returns the entire response body(same with err.stack).
So basically -- err, err.message and err.stack returns the same output
How do I end up fetching only the value inside a property say message ?
PS:
err.statusCode does return the correct value ie. "500".
err.name returns "StatusCodeError"(not "Error" that I see in the response)
OK, so while not really a Hyperledger Composer question - you could try is it response.getBody() ? This is just an example, not a code snippet to use
var finalHost = "yoururl";
var r = new sn_ws.RESTMessageV2();
r.setHttpMethod("get");
r.setEndpoint(finalHost);
//r.setQueryParameter("locatenow", "true");
r.setBasicAuth(username,password);
var response = r.execute();
var responseBody = response.getBody();
// If response is in json format, directly you can send to client.
// If not then convert it.
return responseBody;
// On client side
var newoptions = JSON.parse(jsonstring);
// returned resp.
You might consider something like Restify for error handling on UI side ?
See README at bottom here https://github.com/restify/errors/blob/master/README.md ->
https://www.npmjs.com/package/restify-errors - see this S/O here

Error handling patterns in Amazon API Gateway and AWS Lambda

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:

Changing status code changes response body in Express

So I have a pretty simple helper function to send errors in my response. I use this function all over my codebase:
exports.error = function (err, res) {
res.send({
success: false,
errorMsg: err.message,
errors: err.errors == null ? [] : err.errors
});
};
I decided to add a status code to it:
exports.error = function (err, res, status) {
res.status(status).send({
success: false,
errorMsg: err.message,
errors: err.errors == null ? [] : err.errors
});
};
If the status is 200 I get the body exactly like the object passed to the send method. The problem is that if status is different from 200 (400 or 500 for example) my response body changes to:
{
config: Object
data: Object
headers: function (d)
status: 500
statusText: "Internal Server Error"
}
And my original response body (the one with success, errorMsg and errors fields) is inside this new response under the data attribute. I have no idea why this is happening but as far as I know I don't have any other custom error handlers in my application. I don't want this behavior and instead I want only my original response body.
I am using the body-parser package, but I believe that it only affects the requests, not the responses.
The response object that you're getting is Angular's response object (see the documentation):
The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
AFAIK, when Angular receives a successful HTTP response (like a 200), it will run any of the default transformations to convert the response to, say, a JS object (from a JSON response).
However, it won't do that when the HTTP response indicates an error. In that case, you will get the above-mentioned response object back.
Thanks to #robertklep I found out that the problem was actually in my Angular code that handled errors. Instead of returning the response body my Angular error handler was returning the error itself.

Resources