Node.js express and mySQL data - mysql

I just started learning JavaScript and Node.JS and I'm really struggling with the concepts of callbacks. So I went on and tried to implement them to get mySQL data pushed to a site.
I began with this:
// connection stuff happened already. Connection works fine.
app.get('/home', function(req,res){
var sql = `select u.* from users u`;
var sql2 = `select u.* from users2 u`;
usersA = [];
usersB = [];
connection.query(sql, function(error, results, fields){
if (error) throw error;
results.forEach(function(user){
usersA.push({
"id":user.id,
"name":user.name,
"lastname":user.lastname
});
});
connection.query(sql2, function(error, results, fields){
if (error) throw error;
results.forEach(function(user){
usersB.push({
"id":user.id,
"name":user.name,
"lastname":user.lastname
});
});
res.render("index", {
usersA: usersA,
usersB: usersB
});
});
});
});
This works. But I feel like this is the wrong approach considering there could be way more than 2 queries.
I need to get both arrays filled before the index is rendered. And I'd like to achieve that a little more straightforward without nesting multiple queries within each other.
Maybe I'm just not used to code looking like that. If this is a valid approach even for 10 or more querys I'll just stick with it. It just feels wrong.
So I started looking into this SO thread and tried to somehow get things to work but it didn't:
function executeQuery(query, callback) {
connection.query(query, function (err, rows, fields) {
if (err) {
return callback(err, null);
} else {
return callback(null, rows);
}
})
}
function getResult(query,callback) {
executeQuery(query, function (err, rows) {
if (!err) {
callback(null,rows);
} else {
callback(true,err);
}
});
}
function getUsers(sqlQry){
getResult(sqlQry,function(err,rows){
if(!err){
return rows;
} else {
console.log(err);
}
});
}
With this prepared I tried the following:
var sql = `select u.* from users u`;
var sql2 = `select u.* from users2 u`;
app.get('/home', function(req,res){
res.render("index", {
usersA: getUsers(sql),
usersB: getUsers(sql2)
});
}
But my usersA/usersB are empty. I guess that's some kind of scoping/asynch problem as getUsers() returns before the query is executed.
From what I've read so far this might be a good place for promises.
I added an answer with my solution.

I found an answer to my question
I found this article on codeburst about Node.js and mySQL using promises and went trying it. So all the credit to them.
app.get('/home', function(req,res){
var db = new Database({
host : "someHost.someDomain",
user : "someUser",
password : "somePassword",
database : "someDatabase"
});
var sql = `select u.* from users u`;
var sql2 = `select u.* from users2 u`;
usersA = [];
usersB = [];
db.query(sql)
.then(rows => {
rows.forEach(function(user){
usersA.push({
"id":user.id,
"name":user.name,
"lastname":user.lastname
});
});
return db.query(sql2)
})
.then(rows => {
rows.forEach(function(user){
usersB.push({
"id":user.id,
"name":user.name,
"lastname":user.lastname
});
});
return db.close();
}, err => {
return db.close().then( () => { throw err; } )
})
.then( () => {
res.render("index", {
usersA: usersA,
usersB: usersB
});
}).catch( err => {
console.log(err);
} )
I don't really get how this works yet but I'll read into it. This allows for handling multiple nested queries with promises which are way easier to handle than simply nesting everything.
If anyone has another way of doing this or feels the need explaining what's happening here I'd very much appreaciate it!
For the sake of completion without visiting the URLs here is the mentioned database class:
class Database {
constructor( config ) {
this.connection = mysql.createConnection( config );
}
query( sql, args ) {
return new Promise( ( resolve, reject ) => {
this.connection.query( sql, args, ( err, rows ) => {
if ( err )
return reject( err );
resolve( rows );
} );
} );
}
close() {
return new Promise( ( resolve, reject ) => {
this.connection.end( err => {
if ( err )
return reject( err );
resolve();
} );
} );
}
}

Related

Nodejs Mysql asynchronous

I'm trying to return my users list from my Mysql database through this endpoint.
The problem is that it return "undefined".
Do you know how to solv this?
Thx in advance :)
app.get("/users", async (req, res) => {
users = await query_my_users_from_db()
// Got "users = undefined" here
console.log(users)
return JSON.stringify(users)
})
async function query_my_users_from_db() {
var users = await db.query("SELECT * FROM `users`", function (err, result) {
if (err) throw err
users = Object.values(JSON.parse(JSON.stringify(result)))
return users
})
}
Since db.query is callback based, you should change your code into:
function query_my_users_from_db(callback) {
db.query("SELECT * FROM `users`", callback)
}
remove all the async\await since you are not using any promises!
then define your callback
const usersHandler = (err, result) => {
... do what you want with your users ...
}
and use it like this:
query_my_users_from_db(usersHandler)
Another option is to use some promise-based wrapper package for MySQL, there is plenty or use node util.promisify() (https://www.geeksforgeeks.org/node-js-util-promisify-method/) and then use async\await and remove the callback altogether.
Hope that it helps
I used this and it's working like a charm.
db.promise = (sql, params) => {
return new Promise((resolve, reject) => {
db.query(sql, params, (err, result) => {
if (err) {
reject(new Error())
} else {
resolve(result)
}
})
})
}
async function connection(query) {
const result = await db.promise(query)
return result
}
app.get("/users", async (req, res) => {
users = await connection("SELECT * FROM `users`")
return users
})

I'm trying to use mysql for multi guild for discord.js

I'm trying to make a multi guild command and I tried to make it but it kept get errors as you can see here is my code I'm using:
client.on('ready', () => {
client.guilds.cache.forEach((guild) => {
connection.query(
`SELECT prefix FROM guilds where guildID = '${guild.id}' AND ownerID = '${guild.ownerID}'`,
function(error, results, fields) {
if (error) throw error;
console.log(results[0].prefix);
}
);
});
});
How can I solve it?
ok i solved it :D
client.on('ready', () => {
client.guilds.cache.forEach(guild => {
connection.query(`SELECT prefix FROM guilds where guildID = '${guild.id}' AND ownerID = '${guild.ownerID}'`, function (error, result, fields) {
if (error) throw error;
prefixx.set(guild.id, result[0].prefix);
});
});
});

Array push doesn't work in promise node.js

I have previously attempted to wrap this code in callbacks, async what ever the language has to offer. However, I am still getting nowhere.
The problem is, that members remains empty, even though it should be pushed with info.
channels however, works fine.
Weirdly, the
console.log(values);
prints before the
console.log(result);
Interestingly though,
console.log(result)
does have the correct data, but where I do
console.log(members)
it is empty.
I have tested, the query is all correct, it is literally a problem with the pushing and getting the result earlier than it currently is returned (I assumed Promises would mean things would be more in order, but maybe my understanding is wrong).
Here is the full code:
module.exports.controller = (query, helper, cache, Database, mysql, config) => {
return async (req, res) => {
let zone = req.body.zone;
let returning = {};
if(!zone){
return res.json(helper.responseJson(false, 'Missing parameter "zone"'));
}
function teleport_available(channel_name){
if(channel_name.indexOf("Nadaj / UsuĊ„") === -1){
return true;
}else{
return false;
}
}
await mysql.query("SELECT * FROM flamespeak_pbot.zones WHERE zone = '"+ zone +"'", async (err, row) => {
if (err) throw err;
row = row[0];
if (row.length == 0) {
return res.json(helper.responseJson(false, "Zone not found."));
} else {
var channelsPromise = new Promise((resolve, reject) => {
const channels = [];
JSON.parse(row.assignGroupAdditional_ch).forEach(additionalCh => {
cache.channelList.filter(channel => channel.cid == additionalCh).forEach(mainCh => {
mainCh.propcache.teleport_available = teleport_available(mainCh.propcache.channel_name);
mainCh.propcache.subchannels = [];
cache.channelList.filter(channel => channel.pid == additionalCh).forEach(subCh => {
subCh.propcache.teleport_available = teleport_available(mainCh.propcache.channel_name);
mainCh.propcache.subchannels.push(subCh);
});
channels.push(mainCh.propcache);
});
});
resolve(channels);
});
var membersPromise = new Promise((resolve, reject) => {
let members = [];
query.serverGroupClientList(row.serverGroupID)
.then(serverGroupList => {
serverGroupList.forEach(member => {
var sql = "SELECT * FROM teamspeak_clientDbList WHERE client_database_id = '" + member.cldbid + "'";
mysql.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
members.push(result);
})
});
})
.then(() => {
console.log(members);
resolve(members);
});
});
}
Promise.all([channelsPromise, membersPromise]).then(function(values) {
console.log(values);
returning = {
'channels' : values[0],
'members' : values[1],
'pbot' : row,
};
res.send(helper.responseJson(true, returning));
});
});
};
};
With respect, your mixture of callbacks, promises, and async / await is very hard to follow. You'd be wise to simplify it. (Or risk having your name cursed by the person who must maintain this code later.)
The callback in your second mysql.query() method is called whenever MySQL wants (whenever the query finishes or fails). But you don't resolve any promise or return from an async method within that callback's code. So, your console.log(result) shows a correct result, but your other code doesn't get access to that result; your Promise.all resolves before that method is called.
Something like this would, in general, be the right thing to do:
var sql =
"SELECT * FROM teamspeak_clientDbList WHERE client_database_id = '" +
member.cldbid + "'";
mysql.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
members.push(result);
resolve (result); // <<< add this line
})
But in your case it probably won't work: your function query.serverGroupClientList() seems to return a Promise. You didn't show us that function, so it's hard to guess how to weave that into your logic.
query.serverGroupClientList(row.serverGroupID)
.then(serverGroupList => {
let members = [];
serverGroupList.forEach(member => {
var sql = "SELECT * FROM teamspeak_clientDbList WHERE client_database_id = '" + member.cldbid + "'";
mysql.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
members.push(result);
})
});
resolve(members);
})
Try this
SELECT * FROM teamspeak_clientDbList
While iterating serverGroupList, the db Query on each element is asynchronous. Hence empty members.
Moreover foreach expects a synchronous function so you will have to use for-loop or for-of to use async-await feature.
var membersPromise = new Promise((resolve, reject) => {
let members = [];
query.serverGroupClientList(row.serverGroupID)
.then(async (serverGroupList) => {
console.log("serverGroupList: ", serverGroupList);
for (let index = 0, arrLen = serverGroupList.length; index < arrLen; index++) {
let member = serverGroupList[index];
var sql = "SELECT * FROM teamspeak_clientDbList WHERE client_database_id = '" + member.cldbid + "'";
await mysql.query(sql, async function (err, result) {
if (err) throw err;
console.log(result);
members.push(result);
});
}
console.log(members);
resolve(members);
});
});
Nested Code can be improved if you write a wrapper function for db queries which returns a promise.
function fireQuery(mysql, query, params = []) {
return new Promise((resolve, reject) => {
return mysql.query(query, params, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
}
// ex -1
let rows = await fireQuery(mysql,query,params);
if(rows.length == 0) {} // your code
// ex-2
let member = await fireQuery(mysql,query2,params2);
members.push(member);

Custom express-validator

My custom-validator always return true although when result is not undefined and i have returned false in that case
const validatorOptions = {
customValidators: {
not_exist: (inputParam) => {
let qry = 'select * from Users where email = ?';
let exist= db.query(qry, inputParam, (err, result) => {
if (err)
throw err;
if (result) {
console.log("Exist");
return false;
} else
return true;
});
return exist;
}
}
};
I tried using Promise, but still it's not working.
const validatorOptions = {
customValidators: {
isUniqueEmail: (email) => {
function getRecord(email) {
return new Promise(function(resolve, reject) {
let qry = 'select * from Users where email = ?';
db.query(qry, email, (err, result) => {
if (err)
reject(err);
else {
resolve(result);
}
});
});
};
getRecord(email).then(function(res) {
return (res.length == 0);
}).catch((err) => { throw err; });;
}
}
};
req.checkBody('email', 'Email already exist').isUniqueEmail();
That is because you're returning the variable exist which is equal to db.query() (and always a truthy value).
I had some questions about this code (because I don't see how it could run in the current state):
Seems odd to have customValidators as a child to validatorOptions, is there a reason for this?
Is there a reason you're mixing camel and snake case?
Do you think the custom validator method name could be more descriptive as to its' purpose, such as isUniqueEmailAddress?
Why isn't the SQL statement concatenating the argument into the
Do you think this code should implement some SQL input cleansing for security reasons?
Does db.query() really expect 3 arguments??? (I would think it just needs SQL and callback)
Since db.query() is performing an asynchronous operation, have you considered using Promises?

How to promisify a MySql function using bluebird?

Some time ago I decided to switch from PHP to node. In my first projects I didn't want to use any ORM since I thought that I didn't need to complicate my life so much learning another thing (at the moment I was learning node and angular) therefor I decided to use mysql package without anything else. It is important to say that I have some complex queries and I didn't want to learn from sctratch how to make them work using one of the 9000 ORM node have, This is what I've been doing so far:
thing.service.js
Thing.list = function (done) {
db.query("SELECT * FROM thing...",function (err,data) {
if (err) {
done(err)
} else {
done(null,data);
}
});
};
module.exports = Thing;
thing.controler.js
Thing = require('thing.service.js');
Thing.list(function (err,data) {
if (err) {
res.status(500).send('Error D:');
} else {
res.json(data);
}
});
how can I promisify this kind of functions using bluebird ? I've already tried but .... here I am asking for help. This is what I tried
var Thing = Promise.promisifyAll(require('./models/thing.service.js'));
Thing.list().then(function(){})
I have done this way and it is working fine.
const connection = mysql.createConnection({.....});
global.db = Bluebird.promisifyAll(connection);
db.queryAsync("SELECT * FROM users").then(function(rows){
console.log(rows);});
I have never had much luck with promisifyAll and IMO I prefer to handle my internal checks manually. Here is an example of how I would approach this:
//ThingModule
var Promises = require('bluebird');
Things.list = function(params) {
return new Promises(function(resolve, reject) {
db.query('SELECT * FROM thing...', function(err, data) {
return (err ? reject(err) : resolve(data));
});
});
}
//usage
var thinger = require('ThingModule');
thinger.list().then(function(data) {
//do something with data
})
.error(function(err) {
console.error(err);
})
You can also create a function that fires SQL like this :-
function sqlGun(query, obj, callback) {
mySQLconnection.query(query, obj, function(err, rows, fields) {
if (err) {
console.log('Error ==>', err);
// throw err;
return (err, null);
}
console.log(query)
if (rows.length) {
return callback(null, rows);
} else {
return callback(null, [])
}
});
}
Where mySQLconnection is the connection object you get after mysql.createConnection({}).
After that, you can promisify the function and use the promise like below :-
var promisified = Promise.promisify(sqlGun);
promisified(query, {}).then( function() {} );