Node JS [Error: Can't set headers after they are sent.] - node.js

Hi lets say I have code like this:
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if(data) {
return res.send("data already exists");
}
else {
//DO CREATE THE NEW DATA
return modelInstance.save();
}
})
.then(function(data) {
if(data) res.send("new data successfully saved");
})
.catch(function(err) {
console.log(err);
});
});
In current condition if data already exists, there will be error
**[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:344:11)**
Why this error raised? based on my knowledge after res.send() is being called then all the later code below it will not be executed right?
I am return data.save() because I want avoid nesting statement.
UPDATE ALTERNATIVE SOLUTION
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if(data) {
return new Promise(function(resolve, reject) {
return resolve();
});
}
else {
//DO CREATE THE NEW DATA
return modelInstance.save();
}
})
.then(function(data) {
if(data) res.send("new data successfully saved");
else res.send("data already exists");
})
.catch(function(err) {
console.log(err);
});
});

Your second .then() handler will still execute because the promise is not branched, it's chained so the chain keeps going and thus you will be doing two res.send() operations and that's what causes the error you see.
When you do:
return res.send("data already exists");
You are just returning a value from the .then() handler. Since that value is not a rejected promise, the promise stays resolved and the next .then() handler in the chain will execute.
You could do this:
router.get('/blablabla', function (req, res, next) {
model.findOne({condition: req.body.condition}).then(function (data) {
if (data) {
return res.send("data already exists");
} else {
//DO mMODIFY THE DATA AND THEN SAVE THE DATA
return data.save().then(function () {
res.send("new data successfully saved");
});
}
}).catch(function (err) {
console.log(err);
});
});
Or, you could do this (modification of your second try - assumes that data.save() returns a promise that resolves to a value):
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if (!data) {
//DO mMODIFY THE DATA AND THEN SAVE THE DATA
return data.save();
}
return data;
})
.then(function(data) {
if(data) res.send("new data successfully saved");
else res.send("data already exists");
})
.catch(function(err) {
console.log(err);
});
});

Related

How to separate the method to the route

I had this route and it worked perfectly
router.get('/api/User/:id',async(req,res)=>{
try {
const{id}=req.params;
let result =await pool1.request()
.input('Iduser', sql.Int, id)
.execute('GetUser')
res.json(result);
}
catch (err) {
res.json({ error: 'Does Not exist' })
}
});
But I want to separate the function and leave the route as clean as possible, try to separate it as follows but I get the following error: TypeError: one is not a function
Route
router.get('/api/User/:id', async(req,res)=>{
try {
res.json((await one(req.params.id))[0]);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
})
Function
const one = async(id)=>{
return new Promise((resolve,reject)=>{
pool.request()
.input('Iduser', sql.Int, id)
.execute('User')((err,results) =>{
if(err){
return reject(err);
}
resolve(results);
});
});
}
What is my mistake, am I calling the function wrong?
to make your code cleaner you can do this :
const getUserById =async(req,res)=>{
try {
const{id}=req.params;
let result =await pool1.request()
.input('Iduser', sql.Int, id)
.execute('GetUser')
res.json(result);
}
catch (err) {
res.json({ error: 'Does Not exist' })
}
}
router.get('/api/User/:id',getUserById);
also to make it cleaner more you can do it like this
export const asyncHandler = (fn) => async (request, response, next) => {
try {
return await fn(request, response, next);
} catch (error) {
return next(error); // or response.json({ error: 'Does Not exist' })
}
};
const getUserById =async(req,res)=>{
const { params: { id } }=req;
const result =await pool1.request()
.input('Iduser', sql.Int, id).execute('GetUser');
return res.json(result);
}
router.get('/api/User/:id',asyncHandler(getUserById));
Thanks mate, I solved why he tells me that it was not a function, I was not calling it well but the way you explain it the route is much cleaner
function
const one = async(id)=>{
return new Promise((resolve,reject)=>{
pool.request()
.input('IdUser', sql.Int, id)
.execute('GetUser',(err,results)=>{
if(err){
return reject(err);
}
resolve(results);
}
)
});
}
route
router.get('api/user/:id', async(req,res)=>{
try {
let result=await m.one(req.params.id);
res.json(result);
} catch (error) {
console.log(error);
res.sendStatus(500);
}
})

Error: Function returned undefined, expected Promise or value

I setup a pubsub function with firebase functions to do some operations every so often in the Firestore. In order to do this I need to make a request to a third party API to get the updated data and then I want to insert that data into the correct collection and document in the Firestore.
const request_promise = require('request-promise')
exports.scheduledFunction = functions.pubsub.schedule('every 2 minutes').onRun((context) => {
console.log('This will be called every 2 minutes')
var username = ''
var password = ''
var options = {
url: 'path.to.api.com',
auth: {
user: username,
password: password
},
json: true
}
request_promise(options)
.then(function (product) {
console.log(product.product_id)
db.collection('products').doc(product.product_id).set(product)
.then(() => {
console.log('Document successfully written')
})
.catch(error => {
console.log('Error writing product to firestore', error)
})
})
.catch(function (err) {
console.log('Failed to get product', error)
})
});
In the code above if I comment out the call to add the data to the Firestore I the correct product_id prints to the console, so I know the request is working but with it left in I get 'Function returned undefined, expected Promise or value'.
You're not returning anything at your execution. A console.log isn't treated as a return
request_promise(options)
.then(function (product) {
console.log(product.product_id)
// add an implicit return here
return db.collection('products').doc(product.product_id).set(product)
.then(() => {
console.log('Document successfully written')
// need's to return something here, using a boolean for simplicity
return true;
})
.catch(error => {
console.log('Error writing product to firestore', error)
// throw will exit the function call
throw Error('Error writing product to firestore', error);
})
})
.catch(function (err) {
console.log('Failed to get product', error);
// throw will exit the function call
throw Error('Failed to get product', error);
})
With proper promise chaining , this looks more clean
rp(options)
.then((product) =>
{
console.log(product.product_id)
// add an implicit return here
return db.collection('products').doc(product.product_id).set(product)
})
.then(() =>
{
console.log('Document successfully written')
// need's to return something here, using a boolean for simplicity
return true;
})
.catch(function (err)
{
console.log('Failed to get product', error);
// throw will exit the function call
throw Error('Failed to get product', error);
});
Also it is not recommended to throw the errors from catch block, catch block are meant to catch errors and handle them and not to throw errors. There are few bits in your code you need to improve but this is not part of this question
Cheers,
Happy Coding

Callback and Promises : cannot read property 'then' of undefined

I am trying to write some code and it work but i ran into a problem where bcrypt makes use of callback and i am trying to resolve it with promise. Here is the code:
`UserSchema.statics.findByData = function(username,password) {
var User = this;
User.findOne({username}).then((user) => {
if(!user){
return Promise.reject();
}
return new Promise((resolve,reject) => {
bcrypt.compare(password,user.password,(err,res) => {
if(res){
resolve(user);
}else {
reject();
}
});
});
});
};`
I call this function from here :
User.findByData(body.username,body.password).then((user) => {
res.send(user);
}).catch((e) => {
res.status(400).send();
});
I get an error: `Cannot read property 'then ' of undefined.
Why i am getting this error?
As stated in the first comment you need to return User.findOne which will be promise. So no need to create another promise. whenever you will return, then will be called with the returned value. You can use Promise.reject() and Promise.resolve() to call catch and then respectively as well.
UserSchema.statics.findByData = function(username,password) {
var User = this;
return User.findOne({username}).then((user) => {
if(!user){
Promise.reject('No user found');
}
bcrypt.compare(password,user.password,(err,res) => {
if(res){
Promise.resolve(user);
}else {
Promise.reject(err);
}
});
});
};

Returning boolean from postgresql pool

I'm currently trying to validate username and password in a function that calls another function from postgresql. My validation should return true in the validate function but it doesn't. The console.log above it works and the parameters I use when I actually use validate() should return true but isn't.
function validate(username, password){
//PG Connect
pool.connect((err, client, done)=>{
if(err){
return console.error('error fetching client from pool', err);
}
client.query('SELECT * FROM users', (err, result)=>{
if(err){
return console.error('error running query', err);
}
for(let i = 0; i < result.rows.length; i++){
if(result.rows[i].username.trim() == username && result.rows[i].password.trim() == password){
console.log("this works");
return true; //this doesn't return true when I call the function
}
console.log(result.rows[i].username.trim() + " " + result.rows[i].password.trim());
}
return false;
done();
});
});
}
The problem is that you're returning within the query's callback instead of the parent function. This means validate() is actually not returning anything.
An easy solution to this would be to make use of either a Promise or a callback on validate. Here is how you could implement the Promise method...
function validate(username, password) {
return new Promise((resolve, reject) =>
pool.connect((err, client, done) => {
if (err) {
return reject(err);
}
client.query('', (err, results) => {
done();
if (err) {
return reject(err);
}
// logic here
resolve(false); // failed validation
});
});
});
}
And you would use it like so...
validate('username', 'password')
.then(valid => console.log(valid))
.catch(err => console.error(err));
Where all your reject() calls are caught in the catch and all the resolve() calls are caught in then.

Mongoose create method has no respond

There's no respone for below save method. I wonder why.
app.post('/something', function (req, res) {
mainModel.save(req.body.data ,function(err,data){
if(err){
console.log(err)
}else{
console.log(data)
}
});
});
Here's how my models/main.js look like http://pastebin.com/eQSFXWb5
You didn't return any response after save. you should return response ie. return res.status(400).send().
you can try it
app.post('/something', function (req, res) {
if(!req.body.data) {
return res.status(400).send({masg: 'Invalid data'});
}
var newMainModel = new mainModel(req.body.data);
newMainModel.save(function(err,data){
if(err){
console.log(err)
return res.status(400).send({masg: 'Error occurred'});
}
console.log(data);
return res.status(200).send(data);
});
});

Resources