Node.js get mysql data and append to object [duplicate] - javascript

I have the following function that gets a hexcode from the database
function getColour(username, roomCount)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
return result[0].hexcode;
});
}
My problem is that I am returning the result in the callback function but the getColour function doesn't return anything. I want the getColour function to return the value of result[0].hexcode.
At the moment when I called getColour it doesn't return anything
I've tried doing something like
function getColour(username, roomCount)
{
var colour = '';
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err) throw err;
colour = result[0].hexcode;
});
return colour;
}
but of course the SELECT query has finished by the time return the value in colour

You have to do the processing on the results from the db query on a callback only. Just like.
function getColour(username, roomCount, callback)
{
connection.query('SELECT hexcode FROM colours WHERE precedence = ?', [roomCount], function(err, result)
{
if (err)
callback(err,null);
else
callback(null,result[0].hexcode);
});
}
//call Fn for db query with callback
getColour("yourname",4, function(err,data){
if (err) {
// error handling code goes here
console.log("ERROR : ",err);
} else {
// code to execute on data retrieval
console.log("result from db is : ",data);
}
});

If you want to use promises to avoid the so-called "callback hell" there are various approaches.
Here's an example using native promises and the standard MySQL package.
const mysql = require("mysql");
//left here for testing purposes, although there is only one colour in DB
const connection = mysql.createConnection({
host: "remotemysql.com",
user: "aKlLAqAfXH",
password: "PZKuFVGRQD",
database: "aKlLAqAfXH"
});
(async () => {
connection.connect();
const result = await getColour("username", 2);
console.log(result);
connection.end();
})();
function getColour(username, roomCount) {
return new Promise((resolve, reject) => {
connection.query(
"SELECT hexcode FROM colours WHERE precedence = ?",
[roomCount],
(err, result) => {
return err ? reject(err) : resolve(result[0].hexcode);
}
);
});
}
In async functions, you are able to use the await expression which will pause the function execution until a Promise is resolved or rejected. This way the getColour function will return a promise with the MySQL query which will pause the main function execution until the result is returned or a query error is thrown.
A similar but maybe more flexible approach might be using a promise wrapper package of the MySQL library or even a promise-based ORM.

Related

Export an array containing mysql results to another javascript file

I making use of node.js + mysql in my app. I would like to export an array containing results/rows of an SQL select statement to another javascript file and display the values stored in the array.
stats.js
var phonenum='0718900000'
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql,[phonenum],function(err,results){
if (err){
throw err;
};
/*if (results==0)
{
let dealersMessage="❌ User not found"
}*/
console.log(results)
var datastatagentStore=[];
results.forEach(item =>{
module.exports=datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
})
app.js
var{datastatagentStore}=require('../functions/stats.js')
console.log('User details '+datastatagentStore);
Based on a comment above:
My goal is to execute the query and display the results to the user every time the user selects the 'view my details' button.
Then you don't want to export the results, you want to export the function which performs the query. That function would return [a Promise which resolves to] the results.
If conn.query returns a Promise then it might look something like this:
var getUserDetails = function (phonenum) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
return conn.query(sql, [phonenum], function(err, results) {
if (err) {
throw err;
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
return datastatagentStore;
});
};
module.exports = getUserDetails;
Though I don't know if conn.query does return a Promise. If it doesn't, you can manually create one:
var getUserDetails = function (phonenum) {
return new Promise(function (resolve, reject) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql, [phonenum], function(err, results) {
if (err) {
reject(err);
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
resolve(datastatagentStore);
});
});
};
module.exports = getUserDetails;
Note the use of resolve and reject here to manually complete the Promise. But however you structure it, the overall point is that your module export wouldn't be the results of having executed the query, it would be the function to execute the query. And since executing the query is an asynchronous operation, that function should return a Promise.
Then you can use it like any other asynchronous operation:
var datastatagentStore = require('../functions/stats.js');
datastatagentStore('0718900000').then(function (details) {
console.log('User details ', details);
});

ExpressJS MySql Pool Connection Query Return

I am just starting in ExpressJS. So here console.log works, so if I make a call, I do see it in terminal result dataset printed. But, it doesn't return it as response. I do console.log and return but still get no data in response..
index.js
...
app.get("/", async (req, res) => {
let data = await queries();
res.json(data);
});
...
Also tried
...
app.get("/", (req, res) => {
res.json(queries());
});
...
queries.js
function main(sql) {
return pool.getConnection((err, connection) => {
if (err) throw err;
return connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
return rows;
});
});
}
function queries() {
let Q = "SELECT * FROM `items`";
return main(Q);
}
I believe the problem is in the function main() if you do see the result in the terminal. Your functions in the index.js file seems okay. The issue is probably due to the promise not being sent back from the main() function in queries.js file. You need to do 2 things -
Wrap the functionality of the main() in a Promise and return that promise. The callback function of the Promise object takes resolve(use this to return result) and reject(use this to return error).
As the main() will return a promise, you queries() function needs to be async, therefore needs to await the return from the main().
I have attached an Image to show how I implemented this for a DNS-lookup function. You will find some similarities.
// Notice the resolve and reject are functions
function main(sql) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
resolve(rows);
});
});
});
}
async function queries() {
let Q = "SELECT * FROM `items`";
let result = await main(Q);
return result;
}
I believe this should work. Give it a try and good luck!

Promises in JavaScript and NodeJS

I am retriving data from a DB. I am using promise to return the data back to the client when the data is ready:
var http = require('http');
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "******",
database: "heroes"
});
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
});
});
});
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
myPromise.then(result => res.end(result))
}).listen(8080);
I have learned that you are supposed to supply two call-back functions to the Promise and that these are optional arguments in case of success or failure.
When I am calling myResolve() (which doesn't exist in this case), the data is being sent back to the client fine using .then(). Nonetheless, when I am not calling myResolve() something doesn't work and the page is being refreshed forever.
A. If these are indeed callbacks, how can I call a non exsiting function such as myResolve() ? Why I can't extract result without calling this function ?
B. How can I execute the database query only after there is a request on the 8080 port ?
When I am trying to insert the promise into a function, I can't use .then (I am assuming the function by itself doesn't have any promise to give obiously).
Code Edit for the second question:
function connectToDB()
{
let myResult;
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
myResult = result;
});
});
});
return myResult;
}
Final code edit after wraping the promise with a function:
function connectToDB()
{
let myPromise = new Promise(function(myResolve, myReject) {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT id, name FROM heroes", function (err, result, fields) {
if (err) throw err;
console.log(result);
myResolve(JSON.stringify(result));
});
});
});
return myPromise;
}
Edit:
The questions are still valid, regardless of the use of res.send or res.end, as far as I understand it has got nothing to do with what I am asking about.
To answer these questions in order:
The function 'myResolve' does exist, as it is being passed in when you create the promise: "new Promise(function(myResolve, myReject))". In this code, you are constructing a Promise object and passing a callback function into the constructor. The two parameters in this callback function are themselves callback functions which you are naming 'myResolve' and 'myReject'. Generally, people name them 'resolve' and 'reject', but the name does not matter as long as it is consistent throughout. Functions can be passed through functions just the same as strings and other types of variables.
Right now, you are immediately declaring the 'myPromise' variable. You can defer the query until a request is hit by wrapping the 'myPromise' in a function which returns 'myPromise'. Then, when the request is made, call the new 'myPromiseFunction'.

Promises resolving to undefined with node.js mysql queries

I'm writing a route that takes in user-input data from a form and uses that data to execute a few separate queries on my database. That is all well-tested, and I know I can get the user input, query the database, and return the results. The part giving me trouble, however, is that I want to randomly select a few results out of all of the returned data, so I am pushing the results into one big array. But since the queries execute asynchronously, the only way to access the data is in the callback and of course if I tried to push the rows in the callback, I still can't access that array outside of the callback functions because that code will execute before the queries are even run. So I'm attempting to use JavaScript async/await syntax to do the queries, as follows:
module.exports = {
search: (req, res) => {
function saveResults(resultsObj) {
Object.keys(resultsObj).forEach(function (key) {
var row = resultsObj[key];
var song = row.song_name
resultsArray.push(song);
return resultsArray;
});
}
async function queryForSong(){
var songGenre;
getSongGenre = "The queries aren't important, I know they work as expected";
await mysqlConnection.query(getSongGenre, (err, rows) => {
if (err) throw err;
songGenre = row[0].song_genre;
songSearchQuery = "SELECT query";
mysqlConnection.query(songSearchQuery, (err, rows) => {
if (err) throw err;
return saveResults(rows);
});
});
}
async function queryForArtist() {
var artistID;
getArtistID = "SELECT query";
await mysqlConnection.query(getArtistID, (err, rows) => {
if (err) throw err;
artistID = rows[0].artist_id;
console.log(rows, artistID);
artistSearchQuery = "SELECT query";
mysqlConnection.query(artistSearchQuery, (err, rows) => {
if (err) throw err;
return saveResults(rows);
});
});
}
async function queryForGenre() {
genreSearchQuery = "SELECT query";
await mysqlConnection.query(genreSearchQuery, (err, rows) => {
if (err) throw err;
return saveResults(rows);
});
}
async function queryForDecade() {
decadeSearchQuery = "SELECT query";
await mysqlConnection.query(decadeSearchQuery, (err, rows) => {
if (err) throw err;
return saveResults(rows);
});
}
var resultsArray = [];
var artist = req.body.searchArtist;
var song = req.body.searchSong;
var genre = req.body.searchGenre;
var decade = req.body.searchDecade;
if (song) {
queryForSong().then((value) => console.log(value)); //these all log undefined
}
if (artist) {
queryForArtist().then((value) => console.log(value));
}
if (genre) {
queryForGenre().then((value) => console.log(value));
}
if (decade) {
queryForDecade().then((value) => console.log(value));
}
}
}
Then the idea is to save the resolved rows from the promises into one big array and randomize it from there. My only thought of why this isn't working is that it could be because the return statements are located inside the callback functions? I have also tried it using promises directly as this post explains, by writing all of my async functions like this instead:
function queryForGenre() {
genreSearchQuery = "SELECT query";
return new Promise(function (resolve, reject) {
mysqlConnection.query(genreSearchQuery, (err, rows) => {
if (err) reject(err);
resolve(saveResults(rows));
});
});
}
But either way, the console.log statements in the .then still print undefined, meaning my promises aren't resolving to the saveResults(rows) value that I give it. Do I need to make the saveResults function asynchronous as well? From my understanding of promises, this seems like it should work. What am I missing? Or is there a simpler way to do this?
Your functions queryForSong, queryForDecade, etc are using await, but the mysql driver query function accepts a callback and does not return a promise. Also, the return inside your callback is scoped within the callback, not scoped with respect to your outside async function. Instead wrap the function within a promise, resolve and reject where necessary. Also avoid using await unless absolutely necessary because using it blocks the event loop. I have given an example for how to wrap with promise for queryForSong
function queryForSong(){
return new Promise((resolve, reject) => {
let songGenre;
const getSongGenre = "The queries aren't important, I know they work as expected";
mysqlConnection.query(getSongGenre, (err, rows) => {
if (err) reject(err);
else {
songGenre = row[0].song_genre;
const songSearchQuery = "SELECT query";
mysqlConnection.query(songSearchQuery, (err, rows) => {
if (err) reject(err);
else
resolve(saveResults(rows));
});
}
});
});
}
So it turns out it was the saveResults function causing me issues. I don't totally understand why this would fix it, but instead of resolving to the value of the saveResults function, I instead tried just including the saveResults body in each function that returns a promise and that works. For example,
function queryForSong() {
var songResultsArray = [];
var songGenre;
getSongGenre = "SELECT query";
return new Promise(function (resolve, reject) {
mysqlConnection.query(getSongGenre, (err, rows) => {
if (err) reject(err);
var row = rows[0];
songGenre = row.song_genre;
songSearchQuery = "SELECT query #2";
mysqlConnection.query(songSearchQuery, (err, rows) => {
if (err) throw err;
//this is the body of the saveResults function
Object.keys(rows).forEach(function (key) {
var row = rows[key];
var song = row.song_name;
songResultsArray.push(song);
});
resolve(songResultsArray);
});
});
});
}

Best practices in context of asynchronous Javascript when calling functions in functions?

I am trying to call two functions and pass the output of the first function as a parameter into the second.
Function 1:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err)
throw err;
if(user)
callback(null, user.statistics);
});
}
Function 2:
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err)
throw err;
if(statistics)
callback(null, statistics.game);
});
};
I am trying to execute the second method by passing the output of the first method as a parameter but the asynchronous nature of javascript is messing it up. I have tried implementing promises to no avail.
Can anyone suggest some good javascript practices to deal with calling functions asynchronously when they need each other? Any help would be appreciated.
After fixing the issue I mentioned above, you can call them in sequence like this:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err) callback(err);
if(user) callback(null, user.statistics);
});
};
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err) callback(err);
if(statistics) callback(null, statistics.game);
});
};
someService.getAllStatisticsByUserId(id, (err, statistics) => {
if (err || !statistics) {
// handle error
return;
}
someService.getGameByStatisticsId(statistics.id, (err, game) => {
if (err || !game) {
// handle error
return;
}
// handle game
});
});
However, as noted in Mongoose documentation:
When a callback function is not passed, an instance of Query is returned, which provides a special query builder interface.
A Query has a .then() function, and thus can be used as a promise.
So you can simply rewrite the calls like this:
someService.getAllStatisticsByUserId(id).then(statistics =>
someService.getGameByStatisticsId(statistics.id)
).then(game => {
// handle game
}).catch(err => {
// handle error
});
or convert it into an async/await function:
async function getGameByUserId(id) {
try {
const statistics = await someService.getAllStatisticsByUserId(id);
const game = await someService.getGameByStatisticsId(statistics.id);
// handle game
} catch (error) {
// handle error
}
}
Note that an async function always returns a Promise, so you must await it or chain it with a .then() to ensure completion of the query and resolve the returned value, if any.
It looks like you should be able to write:
getAllStatisticsByUserId("me", (err, stats) => {
getGameByStatisticsId(stats.id, (err, game) => {
console.log(game);
});
});
Here's how it would look if these functions returned promises instead.
getAllStatisticsByUserId("me")
.then(stats => getGameByStatisticsId(stats.id))
.then(game => console.log(game))
Even better, if you're able to use a version of Node that supports async/await then you could write.
let stats = await getAllStatisticsByUserId("me");
let game = await getGameByStatisticsId(stats.id);
console.log(game);
This would mean slightly rewriting the original functions (unless User.findById and Statistics.findById already return promises).
module.exports.getAllStatisticsByUserId = function(id, callback){
return new Promise((resolve, reject) => {
User.findById(id, (err, user) =>{
if(err) return reject(err);
return resolve(user.statistics);
});
});
}

Categories

Resources