Node loop insert with mySQL and Mongodb - mysql

I have a form with one field that allows user to enter multiple developer id via comma delimited (ab1234,bc5678).
Once the form is submitted I perform the following tasks:
Get the the project
Loop through array of developer IDs to get their full name using mySQL
update the project using MongoDB
I'm new and sure this this is possible, The codes I have below is not working for me. Can someone please let me know if the codes below is even close.
const mongoose = require('mongoose'
const mysql = require('mysql');
// Create mySQL connection
const mySQLdb = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root',
database : 'projects'
});
const Project = mongoose.model('project');
router.post('/developerSave', async (req, res) => {
let devList = req.body.dev_ids,
devIdArr = devList.split(','),
rData = {};
// get project
const project = await Project.findById(req.body.projectID);
mySQLdb.connect();
for(var i=0, len=devIdArr.length; i < len; i++) {
let sql = `SELECT CONCAT(first_name, ' ', last_name) as full_name FROM users WHERE id= '${devIdArr[i]}'`;
mySQLdb.query(sql, function (err, results) {
if (err) throw err;
let newDev = {
userId: devIdArr[i],
fullName: results[0].full_name
}
project.developers.unshift(newDev);
await project.save();
});
}
mySQLdb.end();
rData.success = true;
rData.msg = 'Developer was added successfully.';
res.status(200).json(rData);
});

The reason you are seeing this is because your await project.save(); is inside the callback function. Your main function will not wait for all the callbacks to complete and close the db connection. Lets look at the example below
const myCallback = (param, callback) => {
setTimeout(() => {
console.log('callback function', param);
callback();
}, 1000)
}
const myAsync = async () => {
console.log('inside async');
const result = await axios.get('http://google.com/');
for (let i = 0; i < 10; i++) {
myCallback(i, () => {
console.log('this is the actual callback function');
});
}
const result2 = await axios.get('http://bing.com/');
console.log('after second call');
}
myAsync();
The output of this is
inside async
after second call
callback function 0
this is the actual callback function
...
As you can see, the after second call is printed before the callback functions.
To solve this problem, you can wrap your callback function in a promise and resolve that once save is complete.

Related

Node JS MySQL wait for results

I'm quite new to Node JS, and I'm trying to build an API based on MySQL.
In one of my routers I'm trying to inject an insert query and based on it, get the new generated task id from mysql.
The problem is that the second query is not waiting for the response and sometimes I'm getting an error because taskId variable is undefined because it still didn't get the results from the first query.
the problematic variable that is not getting it's value correctly is taskId.
I'm attaching my code for your review, thanks for your help!
As requested: I'm attaching my required moudle as well:
const dotenv = require('dotenv');
const mysql = require('mysql');
dotenv.config();
var connection = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_TABLE,
port: process.env.DB_PORT
});
module.exports = connection;
router.post('/new', auth, async (req, res) => {
const uid = req.body.uid;
const taskName = req.body.taskName;
const description = req.body.description;
const createdDate = req.body.createdDate;
const estimatedDate = req.body.estimatedDate;
const status = req.body.status;
let taskId = '';
addTaskQuery = `INSERT INTO task (title,description,status) VALUES ('${taskName}','${description}','${status}')`;
findTaskIdQuery = `SELECT id FROM task WHERE title = '${taskName}'`;
try {
// Injecting into task table
await connection.query(addTaskQuery, (err, results) => {
if(err) {
console.log(err);
return res.send(JSON.stringify({data: err}));
}
})
// Getting the new inserted task id
await connection.query(findTaskIdQuery, (err, results) => {
if(err) {
console.log(err);
return res.send(JSON.stringify({data: err}));
}
taskId = JSON.stringify(results[0].id);
})
// Injecting into havetask table
await connection.query(`INSERT INTO havetask (id,userId,taskId) VALUES (${taskId},${uid},${taskId})`, (err, results) => {
if(err) {
console.log(err);
return res.send(JSON.stringify({data: err}));
}
})
}
catch(err) {
console.log(err);
return res.status(401).json({ msg: 'An error occured while tried to add task'});
}
})
The mysql package you use does not support Promises (=== it doesn't do async / await). So your await statements don't wait, they just fall through.
You need to try a package that handles async / await. This one might do the trick.

Node callback returns mysql result but i cant print to the user with actions on google for dialogflow

I created an intent to get user information based on the ID he provides as param. Using a mysql module i can process the query and get the result. With a callback i can get the result to the main function but the agent ignores once i pass to a conv.ask(). What am i doing wrong?
This is my first script with node. I tried declaring pesquisar_aluno() in a variable so i could use in the main function but it retuns null.
const express = require('express');
const bodyParser = require('body-parser')
const mysql = require('mysql')
const {
dialogflow,
SignIn,
SimpleResponse
} = require('actions-on-google')
app.intent('pesquisar.alunos', (conv, params) => {
const aluno = params.aluno
conv.ask('Vamos pesquisar')
pesquisar_aluno(aluno,function(result){
var resposta = result
console.log(resposta) // returns the result
conv.ask(resposta) // ignores it
})
console.log(resposta) // returns undefined
})
function pesquisar_aluno(aluno,callback)
{
var connection = mysql.createConnection({
host : process.env.MYSQL_HOST,
user : process.env.MYSQL_USER,
password : process.env.MYSQL_PASS,
database : process.env.MYSQL_DB
})
connection.connect()
var query = `SELECT * FROM aluno WHERE id_aluno = "${aluno}"`
connection.query(query, function (error, results, fields)
{
if(error) throw error
var usuario = `RA =>${results[0].id_aluno} Nome => ${results[0].nome}`
if(callback) return callback(usuario)
})
}
Expect conv.ask(resposta) to print the result to the user but its not printing anything
Edit: Changed to promises. It worked!Thanks to Nick Felker and Prisoner
app.intent('pesquisar.alunos', (conv, params) => {
const aluno = params.aluno
conv.ask('Vamos pesquisar')
let nome = pesquisar_aluno_promise(aluno).then(function(results) {
return results[0].nome
}).catch((err) => setImmediate(() => { throw err; }))
return nome.then(function(result){
conv.ask(result)
})
})
async function pesquisar_aluno_promise(aluno)
{
return new Promise(function (resolve,reject) {
var connection = mysql.createConnection({
host : process.env.MYSQL_HOST,
user : process.env.MYSQL_USER,
password : process.env.MYSQL_PASS,
database : process.env.MYSQL_DB
})
connection.connect()
var query = `SELECT * FROM aluno WHERE id_aluno = "${aluno}"`
connection.query(query, function (error, results, fields)
{
if (error) {
return reject(error)
}
resolve(results)
})
})
}
As Nick suggested in the comments, you need to use Promises when you are doing asynchronous operations.
Additionally, however, you need to return that Promise from your Intent Handler so the Intent Dispatcher knows to wait for the result before continuing.
In your case, this can just be adding return, so it might look something like this:
return nome.then(function(result){
console.log(result) //works
conv.ask(result) //should work now
})

discord.js/node.js make code wait until sql query returns result

I am working on a discord.js bot, and I'm storing a bunch of information on various servers in a database. The problem is, that the code doesn't wait for the database to return the results. In the current situation, I'm trying to check if the server specific prefix checks out.
I tried using async and await at various places, but those didn't work. If I could, I'd rather not use .then(), because I don't really want to put all the commands inside a .then().
const { Client, Attachment, RichEmbed } = require('discord.js');
const client = new Client();
const mysql = require("mysql");
const config = require("./config.json")
var con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'botdb'
})
client.on("ready", () => {
console.log("I'm ready")
})
client.on("message", message => {
if (message.author.bot) return;
if (message.channel.type === 'dm') return;
let msg = message.content.split(" ");
let command = msg[0];
let prefix;
con.query(`SELECT * FROM serversettings WHERE ServerID = ${message.guild.id}`, (err, rows) => {
if (err) throw err;
prefix = rows[0].Prefix;
console.log(prefix)
})
console.log(`Prefix: ${prefix}, Command: ${command}`)
if (command === `${prefix}examplecommand`) {
//Do something
}
//Other code that uses prefix and command
}
It should log the prefix first, and then the Prefix: ${prefix}, Command: ${command} part, but it does it the other way around, so the examplecommand doesn't work.
Your result is caused by the fact that what's outside your query callback is executed immediately after the call. Keep in mind the mysql module is callback-based.
Possible Solutions
Place the code inside the callback so it's executed when the query is completed.
Wrap the query in a promise and await it.
function getGuild(guildID) {
return new Promise((resolve, reject) => {
con.query(`SELECT * FROM serversettings WHERE ServerID = '${guildID}', (err, rows) => {
if (err) return reject(err);
resolve(rows);
});
});
}
const [guild] = await getGuild(message.guild.id) // destructuring 'rows' array
.catch(console.error);
console.log(guild.prefix);
Use a Promise-based version of a MySQL wrapper, like promise-mysql. You could use it the same way as the code above, without worrying about coding your own Promises.
const [guild] = await con.query(`SELECT * FROM serversettings WHERE serverID = '${message.guild.id}'`)
.catch(console.error);
console.log(guild.prefix);

Add transaction to sequelize create module

I'm starting with sequelize to create an API and I'm facing an issue with transactions.
My sequelize database configuration looks like this:
var Sequelize = require('sequelize');
var sequelize = new Sequelize(CONFIG.database, env.user,
env.password, {
host: env.host,
dialect: env.dialect,
port: env.port,
operatorsAliases: false
});
var db = {};
fs.readdirSync(__dirname).filter(function (file) {
return (file.indexOf('.') !== 0) && (file !== 'index.js');
}).forEach(function (file) {
var model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function (modelName) {
if ('associate' in db[modelName]) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
Then I have a stockcontroller with functions to save in database like this:
var exports = module.exports = {}
let Stock = require('../models').Stock;
let StockVariant = require('../models').StockVariant;
exports.create = function (req, res) {
const body = req.body;
console.log(body);
Stock.create(body).then(function (stock, created) {})...}
I want to create transaction to save into stockvariant and stock tables in one single transaction and have the option to rollback when error.
Documentation in sequelize doesn't look easy for me to understand as I don't see how to apply this
return sequelize.transaction(function (t) { return User.create({}) })
because t is of course not defined anywhere and my stockcontroller doesn't import sequelize.
So in the end I don't understand the basic concept of how to define that transaction function to create a new stock line.
Thanks for your help!
The sequelize instance needs to be imported to use transactions. It is already exported in your database configuration file with this line db.sequelize = sequelize.
All you need to do is adding it in the current imports :
var exports = module.exports = {}
const Stock = require('../models').Stock; // Prefer const usage to avoid overwritting imports
const StockVariant = require('../models').StockVariant;
const sequelize = require('../models').sequelize;
This could also be done in one line using destructuring :
const { Stock, StockVariant, sequelize } = require('../models');
Now let's come to the transaction. As stated in the documentation, you have two ways of handling them : managed or unmanaged.
Managed transaction
This is done by chaining your asynchronous operations inside the sequelize transaction callback. In this case, if the operations linked to the transaction succeed, the transaction will commit automatically, otherwise it will rollback.
exports.create = function (req, res) {
const body = req.body;
console.log(body);
sequelize.transaction(function(t) {
return Stock.create(body, {transaction: t}) // We pass the transaction as a parameter here !
.then(function(stock, created) {
return StockVariant.create(..., {transaction: t}) // The transaction, again here
})
.catch(function(err) {
// Handle your error...
});
}
Unmanaged transaction
If you want more transparency and/or control over your transaction, you can use an unmanaged transaction. In this case, you must call commit and rollback manually.
exports.create = function (req, res) {
const body = req.body;
console.log(body);
sequelize.transaction
.then(function(t) { // Note the 'then' usage here
return Stock.create(body, {transaction: t}); // We pass the transaction as a parameter here !
.then(function(stock, created) {
return StockVariant.create(..., {transaction: t}); // The transaction, again here
});
.then(function() {
return t.commit();
})
.catch(function(err) {
return t.rollback();
});
}
This could also be done with async / await syntax, which may be more pleasant to read :
exports.create = function (req, res) {
const body = req.body;
console.log(body);
let t; // The variable in which the transaction object will be stored
try {
t = await sequelize.transaction();
const stock = await Stock.create(body, {transaction: t})
await StockVariant.create(..., {transaction: t}) // Whatever parameter you need to pass here
await t.commit();
} catch (err) {
await t.rollback();
}
}

Object is null when reading from JSON in NodeJs

I got this server code running
const fs = require('fs');
const express = require('express');
const app = express();
app.get('/profile/:id', function (req, res) { // A route with a parameter
res.render('profile', {
user: getUserById(req.params.id)
});
});
app.listen(8888, function () {
console.log('Server running on port 8888');
});
function getUserById(userId){
fs.readFile('./database.json', 'utf8', function (err, data) {
var json = JSON.parse(data);
var users = json.users;
return users.find(u => u.id === userId);
});
}
And when calling the route, the function getUserById gets called. In my database, I have this data
{
"users": [
{
"id": 2312,
"name": "Foo Bar",
}
]
}
so the route would be /profile/2312 for example.
req.params.id returns the value 2312.
In the loop at var currentUser = users[0]; currentUser.id will return 2312 and the parameter passed in is 2312.
But when assigning user = currentUser; the object user is null.
Do I miss a module? Is the code wrong?
user object is null because you are returning it before your code actually reads the file.
fs.readFile('./database.json', 'utf8', function (err, data) { }
fs.readFile is asynchronous, so in order to return correct value you have to move the return statement inside fs.readFile block.
Also since getUserById is calling an asynchronous function, you have to call res.render after 'getuserById' finishes executing.
const fs = require('fs');
const express = require('express');
const app = express();
app.get('/profile/:id', getUserById);
app.listen(8888, function () {
console.log('Server running on port 8888');
});
function getUserById(req,res){ // Get a user from the database by userId
const userId = req.params.id;
fs.readFile('./database.json', 'utf8', function (err, data) {
var json = JSON.parse(data); // get the JSON object
var users = json.users; // convert the object to a user array
var match = users.find(u=>u.id.toString()===userId.toString());
//Call render after the asynchronous code finishes execution.
res.render('profile', {
user: match
});
});
}
How does Asynchronous Javascript Execution happen? and when not to use return statement?