Does anyone know why this code gives me the error: "Cannot set headers after they are sent to the client"?
Is there something wrong with my Mongoose Queries?
Thanks for your help!!
router.post('/setriskbydate', (req, res) => {
var username = req.body.username;
var from = new Date(req.body.from);
var to = new Date(req.body.to);
var dates = getDates(from, to);
User.findOne({ username: username }, (err, resp) => {
if (err) {
res.send(err);
}
if (resp) {
var rideIds = resp.ride;
for (let i = 0; i < rideIds.length; i++) {
Ride.findOne({ _id: rideIds[i] }, (error, response) => {
if (error) {
res.send(error)
}
if (response) {
var busnumber = response.busnumber
var date = response.date.split('T')[0];
if (dates.includes(date)) {
console.log(response);
Ride.updateMany({ busnumber: busnumber, date: { "$regex": date }}, { risk: "high" }, (er, re) => {
if (er) {
res.send(er);
}
if (re) {
res.send(re);
}
})
}
}
})
}
}
})
})
Make sure to return all your response statements so that the responses aren't sent more than once, including the headers which cannot be set again after a response.
return res.send(err);
...
return res.send(re);
I think I got it.. Because I am in a for loop I cant do res.send() because the function terminates then. I just replaced the last res.send() with something else and it worked.
Related
I'm trying to use mongoose .select operator with my azure function but it keeps saying TypeError: db.collection(...).findOne(...).select is not a function at db.collection.find.toArray
It returns the user's data in the console, but doesn't filter it down with .select
Why is that?
var MongoClient = require('mongodb').MongoClient;
var Post = require('./model/post');
var mongoose = require('mongoose');
module.exports = async function (context, req) {
let currentPage = 1;
MongoClient.connect(process.env.CosmosDBConnectionString, async (err, client) => {
let send = response(client, context);
if (err) send(500, err.message);
let db = client.db(process.env.dbName);
await db.collection('listings').find(
{
$and: [
{ winnerHasBeenNotified: false },
{ auctionEndDateTime: { $lte: Date.now().toString() } }
]
}
)
.toArray((err, result) => {
console.log("result");
console.log(result);
if (result) {
for (let i = 0; i < result.length; i++) {
db.collection('users').findOne(
{
_id: mongoose.Types.ObjectId(result[i].bidderId)
}
).select("notificationBy").toArray((err, result) => {
console.log("USER RESULT!");
console.log(result);
});
}
}
if (err) send(500, err.message);
send(200, JSON.parse(JSON.stringify(result)));
});
});
};
function response(client, context) {
return function (status, body) {
context.res = {
status: status,
body: body
};
client.close();
context.done();
};
}
find() returns a cursor, which has a select method. findOne() retrieves a single document and returns a promise if no callback is provided.
If you are trying to get just the "notificationBy" field, try passing the fields option to findOne.
I have a PUT request that I'm trying to have hit the backend, but for some reason, it never reaches it. What's odd is the if(req.body.bidderId){} hits no problem, but not the if(req.body.watchingGroup){}
The watching angular service uses identical code to the bidderId so I don't know what's different between the two where only one would reach the endpoint? Whats wrong with the addToWatchList call? I did testing and both console.log statements in code block return the correct value. So the data is ready to be passes, but is never received.
console.log("MADE IT TO LISTINGS BACKEND");
never outputs for watchingGroup scenario
watching.service.ts
addToWatchList(id: string, watchingGroup: string[]) {
const watching: Watching = {
id: id,
watchingGroup: watchingGroup
};
console.log("Here are the values being passed to backend");
console.log(watching.id);
console.log(watching.watchingGroup);
console.log(watching);
return this.http.put(`http://localhost:3000/api/listings/${watching.id}`, watching,
);
}
app.js
app.put("/api/listings/:id", (req, res) => {
console.log("MADE IT TO LISTINGS BACKEND");
if (req.body.biddingGroup) {
console.log("bidding has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
currentBid: req.body.currentBid,
lastBidTimeStamp: Date.now(),
bidderId: req.body.bidderId,
auctionEndDateTime: req.body.auctionEndDateTime,
biddingGroup: req.body.biddingGroup,
lastBidTimeStamp: req.body.lastBidTimeStamp
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
if (req.body.watchingGroup) {
console.log("watching has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
watchingGroup: req.body.watchingGroup
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
});
addToWatchList
addToWatchList(
auctionId: string,
watchingGroup: string[]
) {
this.watchItStatus = true;
this.userId = localStorage.getItem("userId: ");
var unique = watchingGroup.filter(function(elem, index, self) {
return index === self.indexOf(elem);
});
this.uniqueResult = unique;
watchingGroup.push(this.userId);
this.watchListService.addToWatchList(auctionId, this.uniqueResult);
}
As i suspected you're not subscribing to it. It's weird but you need to subscribe to it.
this.watchListService.addToWatchList(auctionId, this.uniqueResult).subscribe(
(res) => {
// Handle success response
console.log("SUCCESS");
},
(err) => {
// Handle error response
console.log("ERROR");
}
);
I want to display chatbot and facebook data at the same time. how to display it? because when I try to run in the browser but it does not appear anything. I've tried to look it up on stackoverflow but did not get the right reference
route.js
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
CB.getAllRecords( function(e, chatbot) {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : chatbot });
});
FBM.getAllRecords( function(e, facebook) {
res.render('cpanel/facebook', { udata : req.session.user, facebook : facebook });
});
}
});
facebook.js
var facebook = db.collection('facebook');
exports.addNewFacebook = function(newData, callback) {
facebook.findOne({accesstoken:newData.accesstoken}, function(e, o) {
if (o) {
callback('accesstoken-taken');
} else {
facebook.insert(newData, {safe: true}, callback);
}
});
}
exports.getAllRecords = function(callback) {
facebook.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
chatbot.js
var chatbot = db.collection('chatbot');
exports.addNewChatBot = function(newData, callback) {
chatbot.insert(newData, {safe: true}, callback);
}
exports.getAllRecords = function(callback) {
chatbot.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
The easier way to manage asynchronous operations in node.js, especially when you have more than one operation that you want to coordinate is to use Promises instead of plain callbacks. And, fortunately, mongodb supports a promise-based interface for all its asynchronous operations now.
So, first change your methods to return a promise instead of taking a callback:
var chatbot = db.collection('chatbot');
exports.getAllRecords = function() {
return chatbot.find().toArray();
}
var facebook = db.collection('facebook');
exports.getAllRecords = function() {
return facebook.find().toArray();
}
Then, you can use those promises with Promise.all() to coordinate:
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
Promise.all([CB.getAllRecords(), FBM.getAllRecords()]).then(results => {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : results[0], facebook: results[1]});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
For a call to just a single function that returns a promise, you can use .then():
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
FBM.getAllRecords().then(results => {
res.render('cpanel/facebook', { udata : req.session.user, facebook: results});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
Acoording to the data available ui should get 2 reults but getting only one since i put res.send in the looop so it is getting ended ,can anyone help me out please.......
exports.getrequestsdetails = function(req, res) {
var params = req.params;
console.log(params)
var record = db.collection('requests');
var item = {
"sent_id": params.id,
"status": 1
}
record.find(item).toArray((err, result) => {
if (err) {
return
}
if (result) {
for (var i in result) {
var id = result[i].recieved_id;
var profile = db.collection('profile');
profile.find({
'_id': new ObjectId(id)
}).toArray((err, resp) => {
if (err) {
return
}
if (resp) {
console.log(resp);
} else {}
});
}
res.send(resp);
} //end of if loop
else {
response = {
status: 'fail',
data: []
};
}
});
}
The problem is in getting the profiles. You are using mongodb's find which is asynchronous. Therefore in your for cycle you start fetching the profiles, but then you send out the res.send well before the fetching of the profiles is finished.
The call back from profile.find(... will be executed after the res.send. Apart from this, the resp variable is inside the find callback and you are trying to res.send it outside.
To deal with this, either you use async or promises. See the below code that uses promises.
var Promise = require('bluebird')
exports.getrequestsdetails = function(req, res) {
var params = req.params;
console.log(params)
var record = db.collection('requests');
var item = {
"sent_id": params.id,
"status": 1
}
record.find(item).toArray((err, result) => {
if (err) {
return
}
if (result) {
var profiles_to_get = []
var profiles = []
for (var i in result) {
var id = result[i].recieved_id;
profiles_to_get.push(get_profile(id, profiles))
}
Promise.all(profiles_to_get)
.then(() => {
res.send(profiles);
})
} //end of if loop
else {
response = {
status: 'fail',
data: []
};
}
});
function get_profile (id, profiles) {
return new Promise(function (resolve, reject) {
var profile = db.collection('profile');
profile.find({
'_id': new ObjectId(id)
}).toArray((err, resp) => {
if (err) {
reject(err)
return
}
if (resp) {
profiles.push(resp)
resolve()
} else {
reject()
}
});
})
}
}
How this works is that it creates a list of profiles to find and stores it in the var profiles_to_get = []. The you use Promise.All(profiles_to_get) which will let you do stuff after all the profiles have been fetched.
You can send only one response back to a request.
Define a variable outside the for loop, append records to it and then send it after the for loop has ended.
exports.getrequestsdetails = function(req, res) {
var params = req.params;
console.log(params)
var record = db.collection('requests');
var item = {
"sent_id": params.id,
"status": 1
}
var resList = [];
record.find(item).toArray((err, result) => {
if (err) {
return
}
if (result) {
for (var i in result) {
var id = result[i].recieved_id;
var profile = db.collection('profile');
profile.find({
'_id': new ObjectId(id)
}).toArray((err, resp) => {
if (err) {
return
}
if (resp) {
console.log(resp);
resList[i] = resp;
}
else{
}
});
}
}//end of if loop
else {
resList = {
status: 'fail',
data: []
};
}
res.send(resList);
});
Don't use for loop in asynchronous mode. Use async module instead like below.
var async = require('async');
exports.getrequestsdetails = function (req, res) {
var params = req.params;
console.log(params)
var record = db.collection('requests');
var item = {
"sent_id": params.id,
"status": 1
}
record.find(item).toArray(function (err, result) {
if (err) {
return
}
if (result) {
var list = [];
async.each(result, function (item, cb) {
var id = item.recieved_id;
var profile = db.collection('profile');
profile.findOne({
'_id': new ObjectId(id)
}, function (err, resp) {
if (err) {
return cb();
}
if (resp) {
list.push(resp);
console.log(resp);
return cb();
}
return cb();
});
}, function (err) {
res.send(list);
});
}//end of if loop
else {
response = {
status: 'fail',
data: []
};
}
});
}
You can push all the resp in list array and send after completing loop.
Like this:
exports.getrequestsdetails = function(req, res) {
var params = req.params;
console.log(params);
var record = db.collection('requests');
var item = {
"sent_id": params.id,
"status": 1
};
record.find(item).toArray((err, result) => {
if (err) {
return err;
}
if (result) {
var list = [];
for (var i in result) {
var id = result[i].recieved_id;
var profile = db.collection('profile');
profile.find({
'_id': new ObjectId(id)
}).toArray((err, resp) => {
if (err) {
return err;
}
else{
list.push(resp);
console.log(resp);
if(i===result[result.length-1]){
res.send(list);
}
}
});
}
} //end of if loop
else {
response = {
status: 'fail',
data: []
};
}
});
};
Hope this work for you
You can add all the list in to an array and finally send the data after the loop
exports.getCityCascade = function (req, res) {
var result = {};
Province.find().exec(function (err, provinces) {
result.provinces = provinces;
var provinceCount = 0;
async.whilst(
function () {
return provinceCount < provinces.length
}
, function (callback) {
City.find({province: provinces[provinceCount].id}).exec(function (err, cities) {
if (err) {
callback(err);
} else {
result.provinces[provinceCount].cities =cities;
}
provinceCount++;
callback(null , result);
});
}, function (err, result) {
if (err) return res.jsonp({message: err.message});
return res.jsonp({
status: '200',
results: result});
}
)
})
}
When I add the cities field to provinces, It seems doesn't work. the response body doesn't contain the filed cities. How to fix it? Any advice would be very helpful.
The problem is just a conflict between variable names: you declared a var result outside Province.find(), but the async.whilst() also uses result as the second argument of its callback function. Just rename one of them and it should work.