Should every db query be wrapped in a try/catch block - node.js

If my controller makes multiple db queries in the same async function should each db query be wrapped in it's own individual try/catch block or is it fine to have all db queries in the same try/catch? What is the reasoning for either option?
All db queries in their own try/catch example:
const confirmEmailVerification = async (req, res) => {
const { token } = req.body;
let user;
try {
const result = await db.query(
'SELECT user_account_id FROM user_account WHERE email_verification_token = $1',
[token]
);
if (result.rows.length === 0) {
return res
.status(400)
.json('Please verify your account by clicking the link in your email');
}
user = result.rows[0].user_account_id;
} catch (err) {
console.error(err.message);
return res.status(500).json('Server Error');
}
try {
const active = await db.query(
'UPDATE user_account SET email_verified = TRUE WHERE user_account_id = $1',
[user]
);
return res.status(200).json({
message: 'Email has been verified, Please login',
});
} catch (err) {
console.error(err.message);
return res.status(500).json('Server Error');
}
};
All db queries in the same try/catch example:
const confirmEmailVerification = async (req, res) => {
const { token } = req.body;
let user;
try {
const result = await db.query(
'SELECT user_account_id FROM user_account WHERE email_verification_token = $1',
[token]
);
if (result.rows.length === 0) {
return res
.status(400)
.json('Please verify your account by clicking the link in your email');
}
user = result.rows[0].user_account_id;
const active = await db.query(
'UPDATE user_account SET email_verified = TRUE WHERE user_account_id = $1',
[user]
);
return res.status(200).json({
message: 'Email has been verified, Please login',
});
} catch (err) {
console.error(err.message);
return res.status(500).json('Server Error');
}
};

This depends on that, if you want the sequence of functions to continue after previous function throws error.
In your case it is useless, because in either errors you finish with res.status(500).json('Server Error').
But sometimes you want to continue, even if some of the functions in the chain throws error, eg.:
let errors = []
try {
f1()
} catch (e) {
errors.push(e)
}
try {
f2()
} catch (e) {
errors.push(e)
}
try {
f3()
} catch (e) {
errors.push(e)
}
If you put this is in one try/catch block, you would stop on the error of f1() and f2() and f3() would not be ran at all.
try {
f1()
f2()
f3()
} catch (e) {
something...
}

Related

Callback error handler didn't stop execute function

I'm trying to develop an API post, in middle execution I have validation such as check name already in use or not. I set error handler callback, it successfully send response 'Already registered', but when I checked to CLI, it show error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I dont know whats wrong, I use this error handler in the past and it seems look ok.
Here is my code in router:
createUserAccount: async function (req, res) {
const programData = req.body;
try {
await service.create(programData, function (code, err, result) {
if (err) {
if(code === 409){
res.status(HTTPSTATUS.CONFLICT).send(err.message);
} else {
res.status(HTTPSTATUS.BAD_REQUEST).send(err.message);
}
} else {
res.status(HTTPSTATUS.CREATED).json(result);
}
})
} catch (e) {
console.log(e)
res.status(HTTPSTATUS.BAD_REQUEST).json("Failed.");
}
Here is my function in my service:
const config = require('#configs/config.json')
const sequelize = require('sequelize');
const SEQUELIZE = new sequelize(config[env]);
module.exports = {
createAccount: async (name, password, callback) => {
try {
let check,
institution_id;
const checkName = await Profile.count(
{
where: {
name: name
}
}
);
//result checkName = 1
if(checkName > 0){
//then successfully execute this condition and
return callback(409, 'Already registered.', null);
//this show in console ----> POST /API/v1/user/profile 409 616.152 ms - 31
}
await Login.create({
username: email,
password: password
}).then(resLogin => {
const response = {
id: resLogin.id,
}
callback(201, null, response);
}).catch( error => {
callback(400, error, null);
})
} catch (e) {
callback(400, e, null);
}
},
create: async (payload, callback) => {
let loginID = null;
let {
profile,
address
} = payload;
let {
name,
email,
password
} = profile;
try {
await module.exports.createAccount(name, password, function (code, error, result) {
if(error){
const res = {message: error};
//what I need is the execution is end in here
return callback(code, res, null);
}
loginID = result.id;
});
//but the fact is it still execute this whole function if got callback error from createAccount()
let transaction = await SEQUELIZE.transaction();
await Address.create(address, {transaction})
.then( async resAddress => {
await transaction.commit();
return callback(201, null, resProfile);
}).catch(async e => {
return callback(400, e, null);
})
} catch (e) {
console.log(e);
callback(e, null);
}
};

Boolean not set to true node.js

I have this code:
at the beginning of the file I initialized the variable (var loginState = false;)
Why console.log give me false, although I change it to true
try {
const client = new SimpleGraphClient(tokenResponse.token);
const me = await client.getMe();
sql.connect(config, async function (err){
if (err) console.log(err);
var request = new sql.Request();
request.query(`SELECT * FROM tradebot.accounts WHERE username='${username}' AND password='${password}'`, async function (err, recordset){
if (err) console.log(err);
console.log(recordset);
if (recordset.recordset.length == 1) {
loginState = true;
} else {
loginState = false;
}
sql.close();
});
});
console.log(loginState);
if (loginState == true) {
await turnContext.sendActivity({
text: 'Work',
attachments: [CardFactory.adaptiveCard(mainmenu)]
});
} else {
await turnContext.sendActivity({
text: 'Dont work',
attachments: [CardFactory.adaptiveCard(internal_login)]
});
}
} catch (error) {
throw error;
}
Put that console.log() inside sql.connect()`, it works the way you are expecting.
sql.connect() is an asynchronous method, so by the time the console.log() happens the sql.connect() doesn't have changed the variable value yet.
loginState = false; // Statement 1
sql.connect(config, async function (err) { // Statement 2
loginState = true;
})
console.log(loginState); // Statement 3
The order of execution of above will be,
Statement 1
Statement 3
Statement 2
So that's why it happens.
Change the code as follows for it to work fine.
try {
const client = new SimpleGraphClient(tokenResponse.token);
const me = await client.getMe();
sql.connect(config, async function (err) {
if (err) console.log(err);
var request = new sql.Request();
request.query(`SELECT * FROM tradebot.accounts WHERE username='${username}' AND password='${password}'`, async function (err, recordset) {
if (err) console.log(err);
console.log(recordset);
if (recordset.recordset.length == 1) {
await turnContext.sendActivity({
text: 'Work',
attachments: [CardFactory.adaptiveCard(mainmenu)]
});
} else {
await turnContext.sendActivity({
text: 'Dont work',
attachments: [CardFactory.adaptiveCard(internal_login)]
});
}
sql.close();
});
});
} catch (error) {
throw error;
}
Hope this helps!
The reason is because you are setting the value to variable inside a callback function :
You have to await your query result. The way you are calling the query is not feasible.
Take a look at - https://www.npmjs.com/package/mysql2
Also change this part -
sql.connect(config, async function (err){
if (err) console.log(err);
var request = new sql.Request();
request.query(`SELECT * FROM tradebot.accounts WHERE username='${username}' AND password='${password}'`, async function (err, recordset){
if (err) console.log(err);
console.log(recordset);
if (recordset.recordset.length == 1) {
loginState = true;
} else {
loginState = false;
}
sql.close();
});
});
Change this to something like this :
try{
let connection = await sql.connect(config);
let query1 = await connection.query(`SELECT * FROM tradebot.accounts WHERE username='${username}' AND password='${password}'`);
if (query1[0].recordset.length == 1) {
loginState = true;
} else {
loginState = false;
}
}catch (err){
// do something here
}

result object returned as undefined when assigned a value

I am creating a results object to return a boolean and a string. it is not being reassigned within the if statement. The rest of the code is working correctly and the password gets updated or the right error message is output to the console
i've tried leaving result undefined before the try catch.
async function passwordUpdate(password, currentPassword, newPwd, email) {
let hashedPassword = await bcrypt.hash(newPwd, 10);
let result = { success: false , message: ' '};
try {
bcrypt.compare(currentPassword, password, async function (err, res) {
if (res) {
let updateResult = await updatePwd(hashedPassword, email);
if (updateResult) {
result = { success: true , message: 'Password was updated successfully.'};
}
else {
logger.info('Password was not updated successfully.');
}
} else {
logger.error('Passwords do not match');
result= { success: false , message: 'Your current password was entered incorrectly'};
logger.error(result.message);
}
});
} catch (error) {
result= { success: false , message: 'Failed to compare passwords'}
}
logger.error('result ', result.message);
return result;
}
code is being called by this method
app.post('/passwordUpdate', async (req, res) => {
let pwd = req.body.password;
let cpwd = req.body.currentPwd;
let newPwd = req.body.newPwd;
let email = req.body.email;
try {
let result = await usersModel.passwordUpdate(pwd, cpwd, newPwd, email);
console.log(result, result.success, result.message);
if (result.success) {
res.status(200).json({error: result.message});
}
else {
res.status(404).json({error: result.message});
}
} catch (error) {
console.log(error);
}
});
logger.error(result.message); this line within the else statement is outputting the message as expected but
logger.error('result ', result.message); after the try/catch is outputting a blank message for result.message
When you're doing this:
let result = await usersModel.passwordUpdate(pwd, cpwd, newPwd, email);
The passwordUpdate function is resolving the promise based on what is in that function's "top level". This means that the callback function of bcrypt.compare doesn't affect the return of passwordUpdate which is why you're not seeing the result you're looking for.
What you can do is wrap the entire thing in a promise and call the resolve/reject inside the bcrypt.compare function.
async function passwordUpdate(password, currentPassword, newPwd, email) {
return new Promise(async function(resolve, reject) {
let hashedPassword = await bcrypt.hash(newPwd, 10)
try {
bcrypt.compare(currentPassword, password, async function(err, res) {
if (res) {
let updateResult = await updatePwd(hashedPassword, email)
if (updateResult) {
resolve({
success: true,
message: 'Password was updated successfully.',
})
} else {
reject({
success: false,
message: 'Password was not updated successfully.',
})
}
} else {
reject({
success: false,
message: 'Your current password was entered incorrectly',
})
}
})
} catch (error) {
reject({ success: false, message: 'Failed to compare passwords' })
}
})
}
You'll also notice I removed the logging, you can put these back if you wish but since this is a promise you can log your errors more centrally from the caller in the .then and .catch or try/catch if you're using async/await.
Also may I suggest that since you'll be able to determine whether it's an error or not based on the resolve and reject, that you can remove the success from the result and only return a string, it'll make the code cleaner:
async function passwordUpdate(password, currentPassword, newPwd, email) {
return new Promise(async function(resolve, reject) {
const hashedPassword = await bcrypt.hash(newPwd, 10)
try {
bcrypt.compare(currentPassword, password, async function(err, res) {
if (res) {
const updateResult = await updatePwd(hashedPassword, email)
if (updateResult) {
resolve('Password was updated successfully.')
} else {
reject('Password was not updated successfully.')
}
} else {
reject('Your current password was entered incorrectly')
}
})
} catch (error) {
reject('Failed to compare passwords')
}
})
}
PS: I didn't test the code pasted above, I only modified your code to better explain it.

Transactions with node pg and foreach loop and async await

I am trying to insert multiple rows in PostgreSQL using node pg. I am using transactions but my query is executing after a response. I tried async await with my function but it is not working
This is my function
addPersons = async (req, res) => {
try {
await db.query("BEGIN");
req.body.forEach((person, index) => {
if (person.id) {
try {
await db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res
.status(Error_code.IdNotFound.code)
.send(Error_code.IdNotFound);
}
const query = `update person set
name = ${person.name},
where id = '${
person.id
}'`;
try {
await db.query(query);
} catch (error) {
try {
await db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res.status(500).send(err);
}
})
await db.query("COMMIT");
res.status(Error_code.Successfull.code).send(Error_code.Successfull);
} catch (error) {
try {
db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res
.status(Error_code.UnableToBeginTransaction.code)
.send(Error_code.UnableToBeginTransaction);
}
}
I also tried calling this function from another function and using foreach on that function but when whenever code detects await or callback in the second function it does not wait and return to the first function.
How can I run this code to add my data into PostgreSQL with transactions
Thanks
Since this is tagged node-postgres, I suggest that you base your code on the A pooled client with async/await example in the node-postgres documentation. I also suggest that you use parameterized queries or a query builder such as mongo-sql. (There are many, but that one's my favourite. 🙂)
It could look something like this:
const { Pool } = require("pg");
const pool = new Pool();
const addPersons = async (req, res) => {
const db = await pool.connect();
try {
await db.query("BEGIN");
const query = `update person set name = $1 where id = $2;`;
// Promise.all() may improve performance here, but I'm not sure if it's safe
// or even useful in the case of transactions.
for (const person of req.body) {
await db.query(query, [person.name, person.id]);
}
await db.query("COMMIT");
} catch (e) {
await db.query("ROLLBACK");
throw e;
} finally {
db.release();
}
};

Return object in async and await in nodejs

I am new in Node.js, I have async method called
async function makeLineStringFromPoints(user) {
...
for (let item of linesGeoJson) {
saveLineIntoDb({
'line': item,
'date': user.date_created,
'user_id': user.uid,
'device_id': user.devid,
}).then((userStored) => {
console.log(userStored); // i received undefined
}).catch((err) => {
console.log(err);
});
}
...
}
in this method I have invoke other async method saveLineIntoDb in this method I an storing user information in database :
async function saveLineIntoDb(user) {
let userStored = user;
try {
await db.result(pgp.helpers.insert(user, cs))
.then(data => {
return await (user); // return user
});
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
Now after storing I want to return user in saveLineIntoDb(user) to }).then((userStored) but always I got undefined.
How can I do that ?
saveLineIntoDb doesn't return the result and mixes async and raw promises. It also prevents errors from being handled in caller function. return await (user) is excessive, it also won't work because it happens inside regular function.
It should be:
async function saveLineIntoDb(user) {
await db.result(pgp.helpers.insert(user, cs));
return user;
}
While caller function doesn't chain promises and doesn't make use of await. If DB queries should be performed in series, it should be:
async function makeLineStringFromPoints(user) {
...
try {
for (let item of linesGeoJson) {
const userStored = await saveLineIntoDb(...);
console.log(userStored);
}
} catch (err) {
console.log(err);
}
}
If DB queries should be performed in parallel, it should be:
async function makeLineStringFromPoints(user) {
...
try {
await Promise.all(linesGeoJson.map(async (item) => {
const userStored = await saveLineIntoDb(...));
console.log(userStored);
}));
} catch (err) {
console.log(err);
}
}
Why you are returning user inside the then? May be can do in this way
async function saveLineIntoDb(user) {
let userStored = user;
try {
await db.result(pgp.helpers.insert(user, cs));
return user; // return user
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
In saveLineIntoDb method try return result of await:
async function saveLineIntoDb(user) {
let userStored = user;
try {
return await db.result(pgp.helpers.insert(user, cs))
.then(data => {
return await (user); // return user
});
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
(This function, you don't need use async/await)

Resources