Dealing with multiple error checks in NodeJS controller - node.js

I need to validate multiple checks in my controller and throw possible errors to my route. Something like this:
Router:
send post var to controller
Controller:
throw error if post.categoria is blank
check in mysql if already exists
throw error if already exists
Router code:
// POST new
router.post('/', function(req, res, next){
// get post var
var post = {categoria: req.body.categoria};
// validate
controller.newCategoria(post).then(
result => {
// return valid result, I'll implement it later
res.json(result);
},
error => {
// return error to frontend
res.json(error);
}
);
});
Controller code:
module.exports.newCategoria = async (post) => {
// throw error if blank
if (post.categoria.length == 0)
throw "categoria is empty"; // portuguese
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
throw err;
if (JSON.stringify(result[0].count) >= 1)
throw new Error("already exists");
});
return "ok";
};
If I send and existing categoria I get: Rethrow non-MySQL errors
I tried to use a promise:
module.exports.newCategoria = async (post) => {
// check if blank
if (post.categoria.length == 0)
throw "blank categoria";
new Promise((resolve, reject) => {
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
return reject(err);
if (JSON.stringify(result[0].count) >= 1)
return reject(new Error("already exists"));
return resolve(result);
});
}).then(
resolve => {
// ok.. deal with it
console.log('ok');
console.log(resolve);
},
error => {
throw error;
}
).catch((error) => {
throw error;
});
return "ok";
};
I don't know how to throw it back to rote, I'm getting this error:
UnhandledPromiseRejectionWarning: Error: already exists
I'm new to NodeJS, I need a good practice for it. Thanks.

You need to return a Promise like so
module.exports.newCategoria = async (post) => {
return new Promise((resolve, reject) => {
// throw error if blank
if (post.categoria.length == 0)
reject(new Error("categoria is empty")); // make sure you are throwing Errors or else node will chastise you
db.query('SELECT COUNT(id) AS count FROM categoria WHERE ?', post, function(err, result) {
if (err)
reject(err);
else if (Number(result[0].count) >= 1) // compare ints to ints, not string to int
reject(new Error("already exists"));
else
resolve(result)
});
})
};
If you want to do more async stuff, here is one way you could refactor
const alreadyExists = x => x && x[0] && x[0].length > 1
const getCategoria = post => {
return new Promise((resolve, reject) => {
db.query(
'SELECT COUNT(id) AS count FROM categoria WHERE ?',
post,
(err, result) => {
if (err)
reject(err)
else if (alreadyExists(result))
reject(new Error('already exists'))
else
resolve(result)
},
)
})
}
module.exports.newCategoria = async post => {
const categoria = await getCategoria(post)
// insert here
}

Related

How to escape from .then() without executing the next statement in Express Js Promise?

By using mysql2 with promise, I have the controller.js as below :
exports.company_add = (req, res) => {
company_model.company_add(admin_email, admin_info).then((result) => { ... })
.catch((err) => { ... })
}
And the model.js as below :
exports.company_add = (admin_email, admin_info) => {
return new Promise((resolve, reject) => {
connectionPool.getConnection((connectionError, connection) => {
if (connectionError) reject(connectionError);
return connection.promise().query('SELECT * FROM admin WHERE admin_email = ?', [admin_email])
.then(([rows, field]) => {
if (rows.length) reject('Email exist');
else return connection.promise().query('INSERT INTO companydb.admin SET ?', [admin_info])
})
.then((result) => {
console.log('result')
if (result[0].affectedRows === 1) resolve(result);
else reject('INSERT FAIL');
})
.catch((err) => {
reject(err);
})
.finally(() => {
connection.release();
})
});
});
};
I wonder how I escape from the .then() statement when the line if (rows.length) occurred, since there is no need to continue with the next .then() for this case if the email already exist in the database. Do I need to throw it and add a .catch() just before the next .then() executed?
One cannot really break out of a then chain other than by nesting or throwing an exception. Yes, in this case you probably should throw, but you don't need to .catch it when you avoid the Promise constructor antipattern and just make it a chained promise:
exports.company_add = (admin_email, admin_info) => {
return connectionPool.getConnection().then(connection => {
return connection.promise().query('SELECT * FROM admin WHERE admin_email = ?', [admin_email])
.then(([rows, field]) => {
if (rows.length)
throw new Error('Email exist');
else
return connection.promise().query('INSERT INTO companydb.admin SET ?', [admin_info])
})
.then((result) => {
console.log('result')
if (result[0].affectedRows !== 1)
throw new Error('INSERT FAIL');
return result;
})
.finally(() => {
connection.release();
})
}, err => {
console.error(err);
throw new Error('CONNECTION FAIL');
});
};

Returned array has undefined value

I am trying to return an array through async/await:
app.get('/users/article/feed',checkAuthenticated,async (request,response)=>{
try{
function executor(){
let articleArray=[]
const sql="SELECT noOfArticles FROM Articles WHERE id=?"
db.query(sql,[request.user.id], (err,result)=>{
if(err) throw err
let noOfArticles=result[0].noOfArticles
for(let i=1;i<=noOfArticles;i++){
const sql1="SELECT ?? FROM Articles WHERE id=?"
let index='article'+i
db.query(sql1,[index,request.user.id],(err,result)=>{
if(err) throw err
articleArray.push(result[0][index])
if(articleArray.length===noOfArticles){
console.log(articleArray); //here the array is printed as expected
return articleArray;
}
})
}
})
}
const resultArray= await executor();
console.log(resultArray); //here the array is undefined
response.render('viewArticles');
} catch(e){
console.log(e);
}
})
The resultArray is always undefined.
I know this is a very old question. I tried checking all the other answers in Stack Overflow but I am confused a lot about this. I am a newbie to js so I couldn't understand it properly. How can I solve this?
You are returning from inside the callback function, which won't work since executor doesn't wait for your queries responses. Return a Promise instead.
function executor() {
return new Promise((resolve,reject) => {
let articleArray = [];
const sql = "SELECT noOfArticles FROM Articles WHERE id=?";
db.query(sql, [request.user.id], (err, result) => {
if (err) return reject(err);
let noOfArticles = result[0].noOfArticles;
for (let i = 1; i <= noOfArticles; i++) {
const sql1 = "SELECT ?? FROM Articles WHERE id=?";
let index = "article" + i;
db.query(sql1, [index, request.user.id], (err, result) => {
if (err) return reject(err);
articleArray.push(result[0][index]);
if (articleArray.length === noOfArticles) {
console.log(articleArray); //here the array is printed as expected
return resolve(articleArray);
}
});
}
});
})
}
Does it return some result if you make the function you are calling async?
async function executor(){
let articleArray=[]
const sql="SELECT noOfArticles FROM Articles WHERE id=?"
db.query(sql,[request.user.id], (err,result)=>{
if(err) throw err
let noOfArticles=result[0].noOfArticles
for(let i=1;i<=noOfArticles;i++){
const sql1="SELECT ?? FROM Articles WHERE id=?"
let index='article'+i
db.query(sql1,[index,request.user.id],(err,result)=>{
if(err) throw err
articleArray.push(result[0][index])
if(articleArray.length===noOfArticles){
console.log(articleArray); //here the array is printed as expected
return articleArray;
}
})
}
})
}
When you return articleArray, you are not returning it from the executor function. Instead you are returning it from the callback that is passed into the db.query function. The () => {} syntax is basically shorthand for function() {} (although there are differences that are outside the scope of this answer).
Perhaps something like this might be helpful (note that I removed the try catch, as I believe such logging should be done at the middleware level in express, which it seems you are using):
app.get('/users/article/feed', checkAuthenticated, async (request, response) => {
return new Promise((resolve, reject) => {
let articleArray = []
const sql = "SELECT noOfArticles FROM Articles WHERE id=?"
db.query(sql, [request.user.id], (err, result) => {
if (err) reject(err)
let noOfArticles = result[0].noOfArticles
for (let i = 1; i <= noOfArticles; i++) {
const sql1 = "SELECT ?? FROM Articles WHERE id=?"
let index = 'article' + i
db.query(sql1, [index, request.user.id], (err, result) => {
if (err) reject(err); // reject the promise if there is an error
articleArray.push(result[0][index])
if (articleArray.length === noOfArticles) {
console.log(articleArray);
resolve(articleArray); // resolve the promise with the value we want
}
})
}
})
})
})

How can I handle SQLITE_CANTOPEN?

I use SQLite with Node.js. If an SQLITE_CANTOPEN error occurs, I want to feedback a message to the user like "Please contact your system administrator.", but I cannot do so because an uncaughtException event will occur if SQLITE_CANTOPEN occurs actually.
How can I return to the caller after an SQLITE_CANTOPEN occurs?
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./sample.db', sqlite3.OPEN_READONLY, err => {
// Comes here when we run db.all() below.
if (err) {
// If opening sample.db fails, SQLITE_CANTOPEN is set to err here,
// and below raise uncaughtException event.
throw err;
}
});
const promise = new Promise(
(resolve, reject) => {
db.all('SELECT userId, userName From user;', (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
}
);
promise.then(value => {
// Perform the normal process.
}, err => {
// SQLITE_CANTOPEN can't catch here.
return 'Please contact your system administrator.';
});
I solved. By wrapping "new sqlite3.Database()" in a Promise, I was able to handle SQL_CANTOPEN before the SQL is executed.
const sqlite3 = require('sqlite3').verbose();
const openDBPromise = new Promise(
(resolve, reject) => {
const db = new sqlite3.Database('./sample.db', sqlite3.OPEN_READONLY, err => {
if (err) reject(err);
else resolve(db);
});
}
);
let db;
try {
db = await openDBPromise;
} catch (err) {
// SQLITE_CANTOPEN can handle here!
return 'Please contact your system administrator.';
}
// Perform the normal process.

Express js promise.all returns undefined

Hi i am newbie to express and promise, i am trying to call multiple asynchronous function using promise.all from express router, but it returns undefined, please guide me to solve the issue .
user.js //routes
var findAllUsersDetails = function(router){
router.post('/api/v1/users/getAllUserFormDetails',
function (req, res) {
Promise.all([
userModel.getAllUsers(req),
userModel.getAllUsers(req),
])
.then((data) => console.log(data))
.catch((err) => console.log(err))
});
}
user.js // models
var userModel = {
getAllUsers : function(req){
var string = "";
var id_company = req['user'].id_company;
var dbConnection = dbConnectionCreator();
var getAllUsers = getAllUsersSqlString(string, id_company);
console.log("ANGEL: finding all employees");
dbConnection.query(getAllUsers, function(error, results, fields){
return new Promise((resolve, reject) => {
console.log(results);
if (error) {
dbConnection.destroy();
console.log("error: ", error);
return reject (err);
} else if (results.length === 0) {
resolve("User not found.");
} else {
resolve(results);
//return (callback({employeeData: results}));
}
})
});
},
}
module.exports = userModel;
Your getAllUsers function is expected to return Promise, but is returning undefined(nothing).
The promise is returned to dbConnection.query but not to the getAllUsers function.
You can try adding return.
return dbConnection.query
if this doesn't work, then dbquery doesn't return the callback which was returned to it.
You might need to find an alternative to solve this.
Let me know if it works.
userModel.getAllUsers(req) should return Promise a.e.:
function getAllUsers(req) {
return new Promise(function(resolve, reject){
//...
});
}
In your case dbConnection.query(getAllUsers, function(error, results, fields) returns Promise therefore you can write something like:
getAllUsers : function(req){
var string = "";
var id_company = req['user'].id_company;
var dbConnection = dbConnectionCreator();
var getAllUsers = getAllUsersSqlString(string, id_company);
console.log("ANGEL: finding all employees");
return dbConnection.query(getAllUsers, function(error, results, fields){
// ^^^
return new Promise((resolve, reject) => {
console.log(results);
if (error) {
dbConnection.destroy();
console.log("error: ", error);
return reject (err);
} else if (results.length === 0) {
resolve("User not found.");
} else {
resolve(results);
}
})
});
},
What i did is i placed the dbconnection within promise function and now i can return promise with the results of dbConnection.query()
getAllUsers : function(req){
var string = "";
var id_company = req['user'].id_company;
var dbConnection = dbConnectionCreator();
var getAllUsers = getAllUsersSqlString(string, id_company);
console.log("ANGEL: finding all employees");
return new Promise((resolve, reject) => {
dbConnection.query(getAllUsers, function(error, results, fields){ //
console.log(results);
if (error) {
dbConnection.destroy();
console.log("error: ", error);
return reject (err);
} else if (results.length === 0) {
resolve("User not found.");
} else {
resolve(results);
//return (callback({employeeData: results}));
}
});
});
Thanks for your immediate reply, actually i understood issue is because of returning promise from your answers.

Fetching Multiple Https request in Nodejs using ejs templates

Getting the first [ const p1] HTTPS request, but unable to fetch the second one [const p2] its showing me undefined.Where im missing
function fetchJSON(url) {
return new Promise((resolve, reject) => {
request(url, function(err, res, body) {
if (err) {
reject(err);
} else if (res.statusCode !== 200) {
reject(new Error('Failed with status code ' + res.statusCode));
} else {
resolve(JSON.parse(body));
}
});
});
}
router.get('/news-and-media',function(req,res,next){
const p1 = fetchJSON('http://example.com/wsplus/abs/123');
const p2 = fetchJSON('http://example.com/blsd/blog_posts/312');
Promise.all([p1],[p2]).then((data) => {
console.log(data[0]); // getting data
console.log(data[1]); // this giving me undefined
res.render("news-and-media", { getdata: data[0],banner:data[1]} );
}).catch(err => console.error('There was a problem', err));
});
Don't use
Promise.all([p1], [p2])
but
Promise.all([p1, p2])
According to the Promise.all() documentation which is saying :
Promise.all(iterable);

Resources