NodeJS/MongoDB - Error: Can't set headers after they are sent - node.js

I got this helper function:
const Account = require('../models/account');
exports.sendInvites = (accountIds, invite, callback) => {
if (!accountIds) {
callback('No account ids provided', null, null);
return;
}
accountIds.forEach((id) => {
Account.findOneAndUpdate({_id: id}, {$push: {organisationInvites: invite}}, callback);
});
};
Then I have this route:
router.post('/organisations', auth.verifyToken, (req, res, next) => {
const organisation = new Organisation({
name: req.body.name,
email: req.body.email,
admins: [req.body.createdBy],
createdBy: req.body.createdBy
});
organisation.save((err, organisation) => {
if (err) {
return res.status(500).json({
error: err,
data: null
});
}
organisationUtils.sendInvites(req.body.invites, {
inviter: req.body.createdBy,
organisation: organisation._id
}, (err, account, response) => {
if (err) {
return res.status(500).json({
error: err,
data: null
});
}
res.json({
error: null,
data: organisation
});
});
});
});
I get a Error: Can't set headers after they are sent. error for the
res.json({
error: null,
data: organisation
});
part but I can't understand why this is happening. I tried looking at the accepted answer here Error: Can't set headers after they are sent to the client, did some digging but couldn't find any specific reason still what's happening in my particular example above. Any ideas?

You are calling the callback multiple times, and so res.json multiple times. Gather the data from all database request then perform an unique res.json.
accountIds.forEach((id) => {
Account.findOneAndUpdate(
{_id: id},
{$push: {organisationInvites: invite}},
callback,
);
});
Something like :
var allData = [];
var nbRequestDone = 0;
var waitAllCallback = function (data, err) {
if (err) {
callback(err);
nbRequestDone = accountIds.length;
return;
}
nbRequestDone += 1;
allData.push(data);
if (nbRequestDone === accountIds.length) {
callback(false, allData);
}
};
accountIds.forEach((id) => {
Account.findOneAndUpdate(..., waitAllCallback);
});

this video covers the Error regarding "Error: Can't set headers after they are sent".
https://www.youtube.com/watch?v=rKTlakY8j2M
In summary of the video, this error shows up when you have an extra callback in your code. Hope this helps. Been struggling with this one too for quite some time.

Related

findById request, cant find my data in mongodb collection

I am creating URL Shortener Microservice application.I have a mongodb cluster that i save my all url links. I am successfully connect to database.I am making post request to save my posted url. Related code is here
app.post('/api/shorturl', (req, res) => {
const bodyUrl = req.body.url;
const something = dns.lookup(
urlParser.parse(bodyUrl).hostname,
(error, address) => {
if (!address) {
res.json({ error: 'Invalid URL' });
} else {
const url = new Url({ url: bodyUrl });
url.save((err, data) => {
res.json({
original_url: data.url,
short_url: data.id,
});
});
}
}
);
});
So, I can save my new url in database succesfully.Here also related cluster after post request
But my problem is with get request. I dont know why i cant find the url links by id. Here also my get request
app.get('/api/shorturl/:id', (req, res) => {
// const id = req.body.id;
Url.findById({ _id: req.body.id }, (err, data) => {
if (!data) {
res.json({ error: 'Invalid URL' });
} else {
res.redirect(data.url);
}
});
});
You need to either use:
Url.findOne({ _id: req.params.id }, (err, data) => {
if (!data) {
res.json({ error: 'Invalid URL' });
} else {
res.redirect(data.url);
}
});
or:
Url.findById(req.params.id, (err, data) => {
if (!data) {
res.json({ error: 'Invalid URL' });
} else {
res.redirect(data.url);
}
});
findOne takes an object as the argument (like you have).
findById just takes the ID as the argument.
You seem to be combining the two options.
Edit: I found another issue with your code, you are trying to pull the id from req.body.id, but in this case, you need to use req.params.id. The code in my post has been updated.

how do I update an object in Sequelize

I am new to Sequelize, and I'm trying to update an object, but it's not working. here is my code
const updateEmployee = async (req, res) =>{
let {full_name, email, phone_number, address} = req.body
const id = req.params.id;
Employee.findOne({
where: { id: id }
})
.then(employee => {
employee.update({
full_name: full_name.full_name,
email: email.email,
phone_number: phone_number.phone_number,
address: address.address
})
})
}
please assist !!
You can use the update() method and add the search scope as second argument. The update() method is a Promise that returns an array with with one or two elements. The first element is always the number of affected rows. See the sequelize API documentation for more details on the update() method.
Your code should look something like this. I have not tested this so you might need to tweak the code a bit:
updateUser: async (req, res) => {
try {
await employee.update(req.body, {
where: {
id: req.body.id
}
})
.then((result) => {
// check the first element in the array if there are rows affected
if (result[0] > 0) {
res.status(200).send({ message: 'data found' });
} else {
return res.status(422).send({ message: 'no data found' });
}
})
}
catch(error) {
console.log(error);
return res.status(500).send({ message: 'updating user failed' });
}
}
When you are using async function. It is best to use await also don't use promise based format this in below api first I am finding user with that id from database if its present it ill update user else it will throw error that there is no data with that id
updateUser: async (req, res) => {
try {
let data = await employee.findOne({
where: {
id: req.body.id
}
});
if (data) {
await employee.update(req.body, { where: { id: req.body.id } });
return res.status(200).send({message: data found});
}
else {
return res.status(422).send({message: no data found});
}
} catch (err) {
console.log(err)
return res.status(500).send({message: Internal server error);
};
}

Using node.js 'util' to promisify is returning an error

I'm trying to create a function in a file to return a promis, which I will call form another file. I'm trying to use the 'util.promisify' to wrap the function, but I'm getting an error. Here is the code and the error:
from my 'checkEmail.js':
const Profile = require('../../models/profile');
const util = require('util');
var exports = module.exports = {};
exports.findEmail = util.promisify(checkEmail());
function checkEmail (email) {
Profile.findOne({ 'emails': { $elemMatch: { email_address: email } } }, (err, userEmail) => {
let conclusion = false;
if (err) {
console.log('Error in looking up an existing email');
} else {
if (userEmail) {
console.log('We found an existing owner for email: ' + email);
conclusion = true;
}
}
return conclusion;
})
}
Calling it on 'profile.js':
router.route('/addemail/:id')
// ADD EMAILS
.put(function (req, res) {
Profile.findOne({ 'owner_id': req.params.id }, function (err, profile) {
if (err)
res.send(err);
EmailCheck.findEmail(req.body.email_address).then((data)=>{
console.log('The answer is: ', data);
});
profile.emails.push({
email_type: req.body.email_type,
email_address: req.body.email_address
})
profile.save(function (err) {
if (err)
res.send(err);
res.json(profile);
});
});
});
The error I'm getting is:
Config for: http://localhost:3000
internal/util.js:272
throw new ERR_INVALID_ARG_TYPE('original', 'Function', original);
Any help would be appreciated.
In order to promisify the function that you pass to util.promisify must:
Take a function following the common error-first callback style, i.e.
taking a (err, value) => callback as the last argument, and returns a version that returns promise
So you can either promisify Profile.findOne, or pass a callback as the last argument to checkEmail
function checkEmail (email, callback) {
Profile.findOne({ 'emails': { $elemMatch: { email_address: email } } }, (err, userEmail) => {
let conclusion = false;
if (err)
return callback(new Error('Error in looking up an existing email'));
if (userEmail) {
console.log('We found an existing owner for email: ' + email);
conclusion = true;
}
return callback(null, conclusion);
})
}
And then you should call it like this:
exports.findEmail = util.promisify(checkEmail);
Otherwise you're passing to .promisify the returned value of checkEmail which is not a function following the style commented above.
You have typo, use util.promisify(checkEmail) instead, parentheses are redundant

nodejs how to do multiple requests

i need to know how i can write my request to make multiple delete.
the second thing is how can i put async function on my code.
i want to delete a campus and in the same time dele the builings with the same id campus in the JSON
app.delete('/campuses/:id', (req, res)=> {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
db.db('').collection('buildings').remove(details, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
});
const details2 = { '_id': new ObjectID(id) };
db.db('').collection('campuses').remove(details2, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
}
);
})
You can delete like this.
app.delete('/campuses/:id', async (req, res)=> {
try {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
await db.db('').collection('buildings').remove(details);
const details2 = { '_id': new ObjectID(id) };
await db.db('').collection('campuses').remove();
res.send(result);
} catch(err) {
return res.json({
success: false,
message: 'error'
});
}
})
You could make sequential functions where the first one calls the second one. You could then pass on variables to the seconds function (ie. your campus ID).
It could look something like this:
const Query1 = (res, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else if (result.recordset[0]) {
let campusID = result.recordset;
Query2(res, campusID, query = 'SELECT bla bla')
}
})
}
const Query2 = (res, campusID, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else {
return res.json({
success: true
});
}
})
}
There are various ways to make async call.
You can use promises.
Async Functions.
Sending response without waiting for other tasks.
If you want to make parallel calls you can use bluebird join function
I like the syntax of async functions better than promises, but I use both depending on the situation.
Here is an example of running functions in order before moving to the next function:
async.waterfall([
function(callback1) {
//Do some work, then callback
if (error) {
callback1(errorGoesHere,null);
} else {
callback1(null,successMessageGoesHere);
}
},
function(callback2) {
//Do some work, then callback
if (error) {
callback2(errorGoesHere,null);
} else {
callback2(null,successMessageGoesHere);
}
}
], function (error, success) {
if (error) {
//show an error
}
//all good return the response, etc.
});
If anything in these functions fail, it automatically call the end function to catch the error.

i want to use async and upsert along with sequelize transaction

upsertAsync: function(req, res, next) {
var datas = req.body;
var user = req.body.user;
async.each(datas, function(data, cb) {
sequelize().transaction({
autocommit: true
}, function(t) {
models.Customer.upsert({
id: data.id,
code: data.code,
createdBy: user.name,
modifiedBy: user.name
}, {
transaction: t
}).then(function(customer) {
cb();
}).
catch (function(error) {
t.rollback();
res.status(500).json(error);
});
}, function(err, data) {
log.debug('error (upsertAsync)', err);
if (err) {
t.rollback();
res.status(500).json(err);
}
t.commit();
res.json({
responseCode: '200',
message: 'Customer has been created..!',
data: data
});
});
});
},
I'm using async.each to insert data into sqlite database at the same time. I want to rollback if any error occurs but it shows error which is [TypeError: Cannot set property 'options' of undefined

Resources