cant set headers after they are set - javascript

Im trying to get the idsFromMongo I saved in my neo4j database and search for those ids in the mongodb to return the objects I want. It works 1 time but then my server crashes and it returns the error Can't set headers after they are sent.
This is my code:.
routes.get('/advertisements/recommended/:brand', function(req, res) {
res.contentType('application/json');
var brandFromUrl = req.params.brand;
var advertisementIds = [];
Advertisement.find({'car.brand': brandFromUrl})
.then(function (ads) {
// res.status(200).json(ads);
ads.forEach(function (record) {
console.log('ids: ' + record._id)
session
.run("MERGE(a:Advertisement {idFromMongo: {idParam}, brand: {brandParam}}) WITH a MATCH(b: Advertisement {brand: {brandParam}}) MERGE(a)-[:SHARED_BRAND]->(b)", {idParam: record._id.toString(), brandParam: brandFromUrl})
.then(function(result) {
session
.run("MATCH (n:Advertisement{brand: {brandParam}}) RETURN (n)", {brandParam: brandFromUrl})
.then(function(result) {
result.records.forEach(function(record){
advertisementIds.push(record._fields[0].properties.idFromMongo);
});
Advertisement.find({
'_id': { $in: advertisementIds}
}, function(err, docs){
res.status(200).json(docs);
})
})
})
});
})
.catch((error) => {
res.status(400).json(error);
});
});
This is my error:
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
at ServerResponse.header (c:\dev\individueel-project\individueel-database\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (c:\dev\individueel-project\individueel-database\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (c:\dev\individueel-project\individueel-database\node_modules\express\lib\response.js:267:15)
at c:\dev\individueel-project\individueel-database\api\advertisement.routes.v1.js:70:35
at model.Query.<anonymous> (c:\dev\individueel-project\individueel-database\node_modules\mongoose\lib\model.js:4046:16)
at c:\dev\individueel-project\individueel-database\node_modules\kareem\index.js:273:21
at c:\dev\individueel-project\individueel-database\node_modules\kareem\index.js:131:16
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)

The problem here is you are executing an array and into the array you are sending the response. So, eventually it will send the responde several times, but only first time will work, after that it will raise an error.
To solve it, you should answer the request after the all promises into array has finished (Promise.all) or if you don't need to wait until the whole array has finished, check if you have already responded and don't do it again.

Related

Error while trying to get realtime data from firestore database code: 'ERR_HTTP_HEADERS_SENT'

i am trying to build an app and i am very new to all this. So I've built a very simple function to get data from firestore and it works fine from a moment. This is the code:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
let result = []
dataRef.onSnapshot(docSnapshot => {
docSnapshot.forEach(doc => {
const data = doc.data()
result.push(data)
})
console.log(result)
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
My problem is that when I try to update any field from the document. It gets updated but I end up receiving this error:
node:_http_outgoing:576
throw new ERR_HTTP_HEADERS_SENT('set');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:372:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:278:15)
at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:162:21)
at C:\Users\paulo\Desktop\sirius-back\functions\src\controller\createDocs.js:70:40
at QueryWatch.onNext (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\reference.js:1914:13)
at QueryWatch.pushSnapshot (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\watch.js:469:18)
at QueryWatch.onData (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\watch.js:353:26) {
code: 'ERR_HTTP_HEADERS_SENT'
}
the app crashes and i have to start it over.
What is the reason of this? How can i get realtime updates from firestore databse after I update something from?
In Express you handle each requests by sending a single response to it. Since res a a response object, you can only send a response to the caller once. But since you're using onSnapshot, your code gets called for every change to the data too.
So initially, you load the data and send a response to the caller and all is 👍
But then when an update is made to the database, your code executes again and tries to send another response to the caller - and this is when you get an error.
The solution is to read the data only one with something like:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
let result = []
dataRef.get().then((docSnapshot) => {
docSnapshot.forEach((doc) => {
const data = doc.data()
result.push(data)
})
console.log(result)
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
Or a bit simplified:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
dataRef.get().then((docSnapshot) => {
const result = docSnapshot.docs.map((doc) => doc.data());
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
Once you've called res.send(...) the request is complete and the client stops listening. Sending further updates to a response is not possible with an Express request/response model like you're using.
Also see:
How to send multiple responses while computing in Express.js?
Sending multiple responses(res.json) with the same response object in Express.js
and more from searching for how to send multiple responses for a single request in express
You'll need to choose an infrastructure that allows a client to keep listening. Heads up: building something like that is quite involved, and you're likely better off if you use the client-side Firestore SDK to implement such realtime listeners

Error: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client, fetch error

This code doesn't allow me to make a fetch request when I invoque sendPushMessages(message) due to HTTP ERRORS but I have no idea why.
The console shows:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Moreover, if I comment: res.status(200).send({shop: shop[0]}); the code isn't still working.
const {models} = require('../models');
const Sequelize = require('sequelize');
exports.findshop = async (req, res, next) => {
sendPushMessages = async (message) => {
try{
let response = await fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: {
Accept: 'application/json',
'Accept-encoding': 'gzip, deflate',
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
let responsed = await response.json();
console.log(responsed);
}
catch (error) {
next(error);
}
}
try {
console.log(req.body.latitud)
console.log(req.body.longitud)
const user = req.user;
const shops = await models.Shop.findAll({
include:[{ model: models.Product, as: 'placements'}]
});
const shop = shops.filter(shp => {
if (Math.abs(shp.latitud - req.body.latitud) <= 0.001 && Math.abs(shp.longitud - req.body.longitud) <= 0.001){
return shp;
}
});
if (shop[0] && user) {
shop[0].placements.forEach(item => {
if (item.dataValues.isoffer){
const message = {
to: user.pushtoken,
sound: 'default',
title: '¡Ofertón! ¡No te lo puedes perder!',
body: item.productname + ' ' + item.price.toString() + '€',
};
sendPushMessages(message);
}
})
res.status(200).send({shop: shop[0]});
} else {
res.status(200).send({shop: null});
}
} catch (error) {
next(error);
}
};
From the code you show (it would be useful to see the top level request handler too so we can see the whole flow of the code), there is an issue if sendPushMessages() has an error. It will call next(err) and keep going in your outer loop which will try to send at least one more response, causing the error message you get for ERR_HTTP_HEADERS_SENT.
You need some error handling upon calling sendPushMessages() so you can stop your loop when it has an error. So, that needs to be fixed. One simple way to fix that would be to remove the catch() block from sendPushMessages() and just let that rejection propagate back to the caller. Then, add an await in front of your call to sendPushMessages(message) like this:
await sendPushMessages(message);
So, when sendPushMessages() rejects, you will hit your own catch block in findshop() and will call next(error) just once and not send any other response.
If the rest of your question is why are you getting an error when you try to access https://exp.host/--/api/v2/push/send', then we can only probably help if you can share what the error is your actually getting. You should be able to add console.log(error) to your catch() handler so you can log the actual error you are getting. In my server code, I always log the actual lowest level error that is occurring because that is often needed to debug why a problem is happening.
This the error that appears:
fetch is not defined
POST /location 500 8.371 ms - 32
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:526:11)
at ServerResponse.header (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/response.js:170:12)
at done (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/response.js:1004:10)
at Object.exports.renderFile (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/jade/lib/index.js:374:12)
at View.exports.__express [as engine] (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/jade/lib/index.js:417:11)
at View.render (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/view.js:135:8)
at tryRender (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/application.js:640:10)
at Function.render (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/home/aitorencinas/Documents/Universidad/TFG/servidor/server_express/node_modules/express/lib/response.js:1008:7)

Record is getting updated but I am still getting this error in console

So I am trying to update the existing records in the MongoDB and records update are being reflected correctly, but in the console, I still see these warnings and errors:
{"name":"Bruce","number":"22222","date":"2020-12-18T20:08:45.446Z","id":"5fdd0c4d72a07e5d0c63c056"}
**** { _id: 5fdd0c4d72a07e5d0c63c056,
name: 'Bruce',
number: '22222',
date: 2020-12-19T06:29:29.022Z,
__v: 0 }
// ^------- this is the record body sent from the frontend and it's being updated correctly.
// but I still get this following errors:
error: Error [ERR_HTTP_HEADERS_SENT]
PUT /api/persons/5fdd0c4d72a07e5d0c63c056 402 99 - 116.533 ms {"name":"Bruce","number":"22222","date":"2020-12-18T20:08:45.446Z","id":"5fdd0c4d72a07e5d0c63c056"}
(node:8288) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]:
Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (E:\fsopen_revision\phonebook\server\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (E:\fsopen_revision\phonebook\server\node_modules\express\lib\response.js:170:12)
at Contact.findByIdAndUpdate.then.catch (E:\fsopen_revision\phonebook\server\index.js:136:11)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:8288) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a
catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Code is used to perform Update:
app.put("/api/persons/:id", (req, res) => {
console.log(JSON.stringify(req.body));
const id = Object(req.params.id);
const body = req.body;
const newPerson = {
name: body.name,
number: body.number,
date: new Date(),
};
Contact.findByIdAndUpdate(id, newPerson, {
new: true,
})
.then((updatedContact) => {
console.log("**** ", updatedContact);
return res.json(updatedContact).send({ message: "Updated" });
})
.catch((e) => {
console.log("error: ", e.name);
res.send(402).json({ name: e.name });
});
});
The frontend is working fine and data is also being updated correctly:
Your error message says: Cannot set headers after they are sent to the client
You can't send response two times. Either use json() method or send() method. Each of them construct headers for the response, send body (payload) and then ends sending response from the server.
// incorrect use of express api
res.json(updatedContact).send({ message: "Updated" });
// change to
res.json({ message: "Updated", contact: updatedContact });
Try this sintax, first check if user id exists in DB, then findByIdAndUpdate with error and response handling:
updateUser: (req, res) => {
const id = Object(req.params.id);
const body = req.body;
const newPerson = {
name: body.name,
number: body.number,
date: new Date(),
};
Contact.findById(id, (err, res) => {
if(err) return res.status(500).send({message: 'Error finding user'});
});
User.findByIdAndUpdate(id, newPerson, {new: true}, (err, updatedContact) => {
if(err) return res.status(500).send({message: 'Error updating'});
if(!userUpdated) return res.status(404).send({message: 'user doesnt exists'});
return res.status(200).send(contact: updatedContact);
});
}

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT] - setInterval & Axios.post error handling

I've tried to handle this warning via Googling. However, as I couldn't solve this warning, I'm asking this question.
The following warning is what I'm facing now:
(node:39452) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:485:11)
at ServerResponse.header
...
at ServerResponse.send
...
at ServerResponse.json
...
at ServerResponse.send
...
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async Timeout.priceAlarm [as _onTimeout]
(node:39452) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
When "priceAlarm" is called, it is okay. However, when "intervalPriceAlarm = setInterval(priceAlarm, 3000, res, chatId);" is done, it shows the warning at axios.post ... catch(error) part.
Does anyone have good idea to handle it?
Thank you.
function priceAlarm(res, chatId) {
axios
.get(url)
.then((response) => {
currentBtcBitfinexUsd = getPrice(
response.data.tickers,
"BTC",
"USD",
"bitfinex"
);
axios
.post( `${telegramBotUrl}${apiToken}/sendMessage`, {
chat_id: chatId,
text:
"\nBitfinex- BTC(USD):" +
currentBtcBitfinexUsd,
})
.then((response) => {
res.status(200).send(response);
})
.catch((error) => {
res.send(error); //****this part shows an error!*****
});
})
.catch((error) => {
console.log(error);
});
}
function intervalAlarm(res,chatId){
if (alarmFlag == 1 && intervalPriceAlarm == null) {
intervalPriceAlarm = setInterval(priceAlarm, 3000, res, chatId);
console.log(intervalPriceAlarm); //need to remove it
}else{
console.log("doing nothing");
}
}
app.post("/", (req,res) =>{
const chatId = req.body.message.chat.id;
const sentMessage = req.body.message.text;
if (sentMessage.match(/price/gi)) {
priceAlarm(res, chatId); //No problem at all
} else if (sentMessage.match(/start/gi)) {
alarmFlag=1;
res.status(200).send({});
} else if(sentMessage.match(/stop/gi)){
alarmFlag=0;
res.status(200).send({});
} else {
res.status(200).send({});
}
intervalAlarm(res,chatId); // here setInterval part.
});
This is a server-side error due to the fact that the server can't re-write HTTP headers of a response it already sent to the client. Consequently, you can't res.send() twice the same res, but you do it in your code
// here the first time
priceAlarm(res, chatId); //No problem at all
// here the second time
intervalAlarm(res,chatId); // here setInterval part.
You'll have to rewrite your code, because that's a base behavior of HTTP that the server can't send data to the client after sending a response already. So that's not an express problem, that's an HTTP limitation. If you want your server to be able to "push" data to your client, you'll have to use websockets instead.

Node/Express route not waiting for Redis server callback

What specific changes need to be made to the code below so that the res.json(...) command is NOT sent until any open calls to the Redis client.somecommand(..) have returned?
The code below is throwing an error related to the client.hmset(uname, { ... } trying to set a response header after res.json(...) was called. When I move the return res.json() commands to right after the end of the client.exists(uname, function(err, reply) { ... } conditional block, instead of their current locations inside the block, the anonymous token value is sent to the client app instead of the generated token value. This indicates that the callbacks to the Redis server have not returned.
How do I change the code below so that the res.json( ... ) commands cannot run until the Redis server callback has been returned? Ideally, there would be some conditional to wait a certain period before sending an error message if the Redis server callback takes too long.
Redis is added to the routes.js file containing all the code below, by adding the following two lines to the top of the file:
var redis = require('redis');
var client = redis.createClient();
Here are the various calls to the Redis server in the code below:
client.exists(uname, function(err, reply) { ... }
client.hgetall(uname, function(err, object) { ... }
client.hmset(uname, { ... }
client.expire(uname, 10);
The complete code for the Node.js/Express.js API route is:
app.get('/user**', function(req, res) {
console.log("You Hit The User Route TOP");
request({
method: 'GET',
url: authServer + '/uaa/user',
json: true,
auth: {
user: null,
password: null,
sendImmediately: true,
bearer: bearerToken
}
}, function (error, response, body) {
if(error){
console.log('ERROR with user request.');
return res.sendStatus(500);
}
else {
var uname = '';var jwtUser = 'empty';var jwtJSON = { "token" : "anonymous" }
console.log(response.statusCode);
if(body['name']){
uname = body['name'];console.log('uname is: ');console.log(uname);
if(uname.length > 0) {
scopesLocal = body['oauth2Request'].scope.toString();
client.exists(uname, function(err, reply) {//Check to see if a Redis key for the user already exists
if (reply === 1) {//a redis key DOES exist
console.log('\"'+uname+'\" exists');
client.hgetall(uname, function(err, object) {//retrieve all the values in the hash/object that we just set
if(object) {
if(object["jwt"]) {
console.log('object[\"jwt\"] is: ');console.log(object["jwt"]);
jwtJSON = { "token" : object["jwt"] };
console.log('jwtJSON is: ');console.log(jwtJSON);
return res.json(jwtJSON);
}
}
});
} else {//a redis key DOES NOT exist
console.log('\"'+uname+'\" doesn\'t exist');
jwtUser = generateJwt(uname, authoritiesLocal);
client.hmset(uname, {//store a hash/object
'AccessToken': body['details'].tokenValue,
'TokenType': body['details'].tokenType,
'Authenticated': body['authenticated'],
'Principal': body['principal'],
'Scopes': scopesLocal.toString(),
'Authorities' : authoritiesLocal,
'jwt' : jwtUser
});
jwtJSON = { "token" : jwtUser };console.log('jwtJSON is: ');console.log(jwtJSON);
return res.json(jwtJSON);
}
client.expire(uname, 10);//set the key to expire in 10 seconds. use this to manage session length
});//end of Redis conditional block
console.log('jwtJSON is: ');console.log(jwtJSON);
} else { console.log('uname is empty!'); }
return res.json(jwtJSON);
}
};
});
console.log("You Hit The User Route BOTTOM");
});
The error message in the nodemon terminal is:
_http_outgoing.js:346
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
at ServerResponse.header (/home/user/nodejs_apps/oauth_seed_app/node_modules/express/lib/response.js:719:10)
at ServerResponse.json (/home/user/nodejs_apps/oauth_seed_app/node_modules/express/lib/response.js:247:10)
at Command.callback (/home/user/nodejs_apps/oauth_seed_app/app/routes.js:112:44)
at normal_reply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:714:21)
at RedisClient.return_reply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:816:9)
at JavascriptRedisParser.Parser.returnReply (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:188:18)
at JavascriptRedisParser.execute (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis-parser/lib/parser.js:413:12)
at Socket.<anonymous> (/home/user/nodejs_apps/oauth_seed_app/node_modules/redis/index.js:267:27)
at emitOne (events.js:90:13)
at Socket.emit (events.js:182:7)
at readableAddChunk (_stream_readable.js:153:18)
at Socket.Readable.push (_stream_readable.js:111:10)
at TCP.onread (net.js:534:20)
I read this posting about the specific error message. And I read this other posting about how to wait for a callback. I also read this posting about Redis callbacks to Node. But I do not see how to apply the answers to either of those other postings to the Redis callback problem in the code above.
The problem was that the return res.json(jwtJSON); commands in the OP were NOT segregated into discrete if...else blocks.
The solution is:
if(something) {
//populate jwtJSON with a real JWT
return res.json(jwtJSON);
} else {
return res.json(jwtJSON);//this leaves the anonymous value
}
Doing this segregation for every nested condition in the above code got rid of the problem.

Categories

Resources