Changing status code changes response body in Express - node.js

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.

Related

How to get response body from Express.js server using Supertest?

I started to write some tests for my application and I have issues to read/get response from the server. I tried many things but nothing really worked, can someone help me please ?
// /api/checkCreds
exports.checkCreds = async function(req, res){
//validation
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
res.sendStatus = 401;
return res.send(JSON.stringify(result));
}
If credentials sent to the server aren't matching, return a response with "Incorrect" message back to the user.
In the test I'm trying to get data from the server to check if properties are matching the expected output.
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.send({username: 'incorrect', password: 'incorrect'})
.expect({error: true, data: "Incorrect"})
.expect(401, done);
});
When ran, test fails because expected properties are different from the response sent by the server, which is an empty object {}.
Any help is appreciated.
You may try changing your first expect to see if you can coax supertest into showing you the actual body that it's comparing to. For example, expect('')
If that doesn't work, there's a version of expect that accepts a function. In that function, you should be able to print out what you are getting in the response body, ie. console.log(res).
It may be that there's some confusion with the JSON return type-- I haven't used that directly. You could try expecting JSON.
Finally, there's a strange paragraph in the documentation that I don't think applies, but I thought I'd mention:
One thing to note with the above statement is that superagent now sends any HTTP error (anything other than a 2XX response code) to the callback as the first argument if you do not add a status code expect (i.e. .expect(302)).
While trying to fix my issue, I noticed that in the HTTP response, Content-Type header was set to text/plain and my server was returning JSON, so that probably was the thing that confused supertest.
I think that res.send() sets the header to text/plain by default and I had to manually set the header value to application/json by using res.type('json'). At that point I was able to read the response body without an issue.
I also learned that res.json() sets the Content-Type header to application/json by default, so you don't need to do it manually like with res.send().
Working code:
// /api/checkCreds
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
return res.status(401).json(result);
}
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.set('Content-type', 'application/json')
.send({username: 'incorrect', password: 'incorrect'})
.expect(401)
.expect(function(res){
console.log(res.body);
})
.end(done);
});
Feel free to correct me if I stated something that isn't quite right.

TypeError: Converting circular structure to JSON when axios response is forwarded

I have an API where I need to call another API inside the controller function. Which I do using Axios, but when I try to attach Axios's response on my original API's response, it throws this error:
TypeError: Converting circular structure to JSON.
Here's a sample code snippet
const userController = async (req,res)=>{
const ax = await axios.put(someURL, reqBody)
res.status(200).json(ax)
}
I know it's nonsensical to do something like this and I know why this error is caused but could somebody explain to me How it's happening in this case?
TypeError: Converting circular structure to JSON is when some property in the object is repeated in another inner object.
Axios response has these properties:
// `data` is the response that was provided by the server
data: {..your real json response..},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the HTTP headers that the server responded with
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {},
// `request` is the request that generated this response
request: {}
If you send the entire axios response object to be converted to json, some property in your real json response could repeat the name of private axios properties like: status, data config, etc
Fix
Try with ax.data instead use the entire axios response ax

Passing custom errors back to AWS API Gateway from AWS Lambda using 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.

Javascript object is undefined, even though it is clearly there

I use a get request (using request npm) to access facebook profile info for my bot, and it clearly gets a body but when I try to access first name in the body, it then says that it is undefined.
The code is as such:
request(`https://graph.facebook.com/${sender_psid}?fields=first_name,last_name,profile_pic&access_token=${PAGE_ACCESS_TOKEN}`, function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body);
console.log(body["first_name"]);
console.log(body.first_name);
The expected output is the body and then Jake, Jake but the output from Heroku can be seen below:
2018-12-23T19:06:47.739490+00:00 app[web.1]: body: {"first_name":"Jake","last_name":"Walker","profile_pic":"https://platform-lookaside.fbsbx.com/platform/profilepic/?psid=XXXXXXX","id":"XXXXXXXXX"}
2018-12-23T19:06:47.739542+00:00 app[web.1]: undefined
2018-12-23T19:06:47.739603+00:00 app[web.1]: undefined
The problem was that facebook was providing a JSON string, but my code was trying to access it as if it was a Javascript object as pointed out by luschn.
The fix to this is adapting the code to using JSON.parse(body) which then converts the JSON string to a Javascript object, which can be accessed through the method I was originally trying.

Express/node.js 204 HTTP code response issue

Here is my code:
.put(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
if(err) return res.send(err);
user.dateEdited = new Date();
user.save(function(err) {
if(err) return res.send(err);
return res.status(204).json(customHTTPcodeReponses.updated(user))
});
});
});
Part of my middleware called customHTTPcodeReponses
updated: function(data) {
return {
code: 204,
status: 'Success',
message: 'Resource updated (or soft deleted)',
data: data
};
}
I as figured out, 204 is not supposed to return any data, so I am not getting any back.
But I would like to have this data to see what was really changed. How could I hande response code then?
Have in mind that if I use
res.status(200).json(customHTTPcodeReponses.updated(user))
data is shown.
If you need some extra explanation, please ask.
yes, you are correct. This http status not allowed messages because it means "No Content". If you send content, use other statuses. For details look at document: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
There is part: "The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields."
If you want to send not content but metadata with addictional info, in this document there is part : "The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.". I think it is formal answer for your question.
If you not so formal, use status '200' with metadata as content.
edit:
to send data in header :
res.header(field, [value])
It is simple. If you need some data back, just use return code 200 and return that data. If you don't need send back any data, use 204. You can set header with 204 to indicate operation status or url like Location with return code 201, but I am not sure this is a good idea.
yes if you have to send response message the use 200 or you can also use 404
because of 204 return no message in body

Resources