Can't set headers after they are sent after first request only - javascript

i'm having a specific problem with the error "Can't set headers after they are sent".
The code is this one:
create: (request, response, next) ->
socket = #app.socket
#
# This method will be used to call the right method inside
# the emails service.
data = JSON.stringify(
Object.assign request.body.data, method: 'server'
)
socket.send data
socket.on 'message', (result) =>
result = JSON.parse(
result.toString()
)
if result.code is 'success'
#model.insertAsync request.body
.then (result) ->
response.json data: result
return
.catch next
return
return
I'm using two servers and socket connection to communication between them. When i want create a email, i send a message for this other server and wait for the result, if the result is "success", i send the data back (i'm using Ember, so i need send the data after saving it). Now the problem: when i send the email for the first time everything works normal, when i try for the second time, a error message shows in my terminal:
Error: Can't set headers after they are sent.
According to express, the error is in this line:
response.json data: result
or in JS:
response.json({data: result});
The code in pure JS:
create: function(request, response, next) {
var data, socket;
socket = this.app.socket;
data = JSON.stringify(Object.assign(request.body.data, {
method: 'server'
}));
socket.send(data);
socket.on('message', (function(_this) {
return function(result) {
result = JSON.parse(result.toString());
if (result.code === 'success') {
_this.model.insertAsync(request.body).then(function(result) {
response.json({
data: result
});
})["catch"](next);
}
};
})(this));
}
Thanks in advance, guys!

Most likely what is happening is you are receiving more than one successful "message" and so it's calling response.json() multiple times.
You could fix this by changing on('message') to once('message') so that the event handler only executes once. However, if that one message is not successful, a response won't be sent. So you may need to either add an else to your if (result.code === 'success') or leave on('message') and introduce some sort of guard variable so that the code inside the if is only executed once.

Related

Postman not getting response from POST

Using Express/Node and Postgres ('pg' package).
Code:
const createEvent = (request, response) => {
console.log(request.body);
const {
type,
location,
current_attendees,
total_attendees,
details,
event_date,
has_car,
has_food,
cover_charge,
contact_email,
} = request.body;
pool.query(`INSERT INTO events (type, location, current_attendees, total_attendees, details, event_date, has_car, has_food, cover_charge, contact_email) VALUES ('${type}', '${location}', ${current_attendees}, ${total_attendees}, '${details}', TO_DATE('${event_date}', 'YYYY-MM-DD'), ${has_car}, ${has_food}, ${cover_charge}, '${contact_email}')`),
(error, results) => {
if (error) {
throw error;
}
console.log(results);
response.status(201).send('Event created');
}
}
Route:
router.post('/events', db.createEvent);
The object gets inserted into the database just fine and I get the request body's console log, but I don't get the results console log and no response is sent back to postman. It just times out.
I'm sending a normal JSON object (Body -> Raw -> Text -> JSON).
Any idea what's going on? Thanks.
In short - when your code runs it get's to pool.query(...) line (which is async function) and it does not wait for the response, because you haven't specified that you want to wait for it.
Solution would be to make createEvent function async:
const createEvent = async (request, response)
and wait for DB response:
await pool.query(...)
Read more about async/await and/or Promises
I see that the url it's http:localhost:8080/api/events you added the /api to the route, or is a sub router in express

Testing 400 status code axios throws error

I'm creating an api test framework for a project I am working on and I'm looking to validate required fields in JSON objects being sent to an endpoint.
I'm trying to send a JSON object with a missing field and expecting a 400 response from my application with a validation message. But when making this call with axios it (somewhat rightly) throws an error as it received the 400.
I want to be able to assert that 400 is expected and to assert on the validation message.
All the examples I've come across are all regarding dealing with the 400 response in the correct way you would if you are not expecting the 400 response, but i am expecting it.
I couldn't seem to find anyone else trying this.
async function createReminder(reminderObject) {
const response = await axios.post(`${config.get('baseUrl')}/reminder`, {
...reminderObject
});
return response;
}
module.exports.createReminder = createReminder;
Here is my working code as it stands. This will make a valid 200 when being used with a valid call.
I need to have the 400 response / validation message be returned in teh response object to be handled in the function that calls this function.
In effect, you want to intercept the response and transform it as you wish. There's a section in the axios docs dedicated to interceptors. As an example of what you can do with it, here is what I've used in a recent project with a Laravel back-end:
axios.interceptors.response.use(
res => Promise.resolve(res.data),
error => {
let message = null;
if (error.response) {
// if error has a data.error property, it's an error formatted by server
if (error.response.data.error) message = error.response.data.error;
else if (error.response.status === 500) message = 'Oops! Something went wrong.';
} else {
// if error has a code property, it's an error defined by axios
if (error.code === 'ECONNABORTED') message = 'Timeout exceeded';
else message = 'Oops! Something went wrong.';
}
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject({ error: message });
}
);
The code above allows me to make axios calls as follows, and always expect the same format:
axios.post('/url', { data: { ... }})
.then(({ jsonProp1 }) => console.log(jsonProp1))
.catch(({ error }) => console.log(error);
Note: if you do create a new axios instance, you need to reinject the response interceptor for this instance too.

How can I get Express to realize there was an error with my Teradata database call?

Currently I have Express running a call against my Teradata database and it acts/performs perfectly when everything works.
However if my Teradata call returns an error I see an output in my console window but I cannot setup an error handler.
I realize this should be extremely basic but I am very new to Express. Any help would be appreciated.
Express Call code:
router.post('/sp_run', function (req, res) {
var sql = "CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);";
console.log(sql);
return teradata.read(sql)
.then((x) => {
console.log(x);
res.send(x);
});
});
Error info I see in my console.
CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);
express_1 | 2019-6-27 21:07:10 - error: [Teradata] Unable to execute query: CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);
express_1 | Unhandled rejection Error: Error running instance method
express_1 | java.sql.SQLException: [Teradata Database] [TeraJDBC 16.20.00.10] [Error 7627] [SQLState HY000] STORED_PROC1:SELECT-INTO returned more than one row.
End game result -- if an error occurs I want to send an email and to send a response back so my front-end isn't just waiting for ever.
We can't send promise from here so just send response with status variable with type boolean
then apply condition on frontend
router.post('/sp_run', function (req, res) {
var sql = "CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);";
console.log(sql);
return teradata.read(sql)
.then((x) => {
return res.json({
status: true,
message: 'All work fine',
result: x
})
}).catch(error => {
//send email about failure
return res.json({
status: false,
message: 'Failed',
error: error.message
})
});
});
Or you can send res with status code and handle using try/catch
On front end
const getData = () => {
// Here axios is use for call a api you can use fetch or anthing.
axios.post('/sp_run', {}).then(data => {
if(!data.status) {
alert(data.error)
} else {
// Play with data
}
})
}

Client is catching generic message from Node server not message I am trying to send

I am not sure what is going on here, but when I am sending back a 400, or some other error request back to the client, node is setting a generic message instead of the one I am sending back. Here is a snippet from my server and what gets put in the UI on the client
Server:
var noContentFound = {
error:true,
message:'No data found for this search'
}
res.status(400).send(noContentFound);
return;
Client:
.catch(function (error) {
let errorFromServer = {
generalServerError:{
error:true,
message:error.message
},
...
};
return errorFromServer;
//both error, and error.message give the message "Request failed with status code 400"
});
So I am sending back in my server noContentFound object with the error message in it through res.status(400).send(noContentFound); But in the client the only thing that is returned in the catch function is Request failed with status code 400 I dont have access to the message I am trying to send back.
Odd thing is, in the network tab on that call on the response tab I can see the object coming through. But error in the catch function does not give me access to that object.
Anyone know why?
EDIT:
Client Call looks like this:
Base Module:
getContent(obj, envBool, baseUrl, httpRequest)
.then(function(data){
//assign to my store object in react and emit the change
});
Where the call is made module:
const getContent(obj, envBool, baseUrl, httpRequest){
//set some variables and data to send
return httpRequest.post(enviUrl,objToSend)
.then(function(response){
returnObj = {
//assign returned data...
}
//...
return returnObj;
})
.catch(function(error){
returnObj = {
//assign and handle error...
}
//...
return returnObj;
});
}

Stripe Charge promise doesn't resolve

I'm making an AJAX call from StripeCheckout.configure({ }) on the token parameter, like this:
StripeCheckout.configure({
...,
token: function(stripeToken) {
$.post( url, {
// post data
}, function(data) {
console.log("data", data);
return data;
});
}
});
At the URL which receives the AJAX call (let's call it /charge), I have this code:
const charge = (req, res) => {
const {
// get AJAX post data, e.g amount, description, STRIPE_TOKEN, etc
} = req.body
return stripe.charges.create({
amount: amount,
currency: 'gbp',
source: STRIPE_TOKEN,
description: description,
})
.then((charge) => {
const {params} = charge // get various parameters from the successful charge data to be passed into book()
return book(params) // promise function which goes to an external provider
.then((data) => {
return data // data returns from book and is then lost between here and the original AJAX callback
})
}).catch((err) => {
console.log(err)
})
}
as you can see from the comments, the data in the charge function is correct, but then between that promise and the original AJAX call, it's lost and the AJAX call gets nothing. If I look at the network request in Chrome devtools, it says that charge received the data but the AJAX call times out as it receives no response data.
I think I've been looking at this too long and have probably made a super easy and stupid mistake.
Figured this out with help from #Bergi in the comments (thanks Bergi!).
Here is the code that worked for me, sending the update from the promise back to the original Ajax request:
const charge = (req, res) => {
// rest of the promise
.then((data) => {
res.json(data)
})
}).catch((err) => {
console.log(err)
})
}
The reason for this is essentially, the AJAX request is hitting the page (/charge in my case), doing the work to get the data, but returning it does nothing, as, just like in a front-end app, unless you are doing something with it (assigning to a variable, running another function with that data, etc), it will get lost. It needs to be outputted to the page, in JSON format, so the request can see it.

Categories