How to create an unused database ID with node.js - mysql

I want to create an ID for my program. The ID is random, but I must make sure that it not already exists in database. To do so, I search the ID, and if the search result is empty, then the ID passes, but if the search finds something, the process needs to start over.
In PHP I can do this logic for my code:
require('somesqlconnection.php');
$repeat = false;
do {
$id = createId(50); // this is my function for creating random with 50 as length for the string
$result = mysqli_query("SELECT * FROM users WHERE id = '$id'", $conn);
$repeat = mysqli_num_rows($result) > 0;
} while ($repeat);
echo $id;
How can I implement the same logic in node.js that is basically executing in asynchronous?
I tried this but didn't work:
const util = require('util');
const sql = require('../sqlconnection.js');
let repeat = false;
const query = util.promisify(sql.query).bind(sql);
do {
let id = createId(50);
(async () => {
const rows = await query(`SELECT id FROM users WHERE id = '${id}'`);
repeat = rows.length > 0;
})();
} while(repeat);
console.log(id);

You could create a function that returns a promise for a new ID like this:
const util = require('util');
const sql = require('../sqlconnection.js');
sql.queryAsync = util.promisify(sql.query);
async function createNewId() {
const newId = createId(50);
const result = await sql.queryAsync('SELECT id FROM users WHERE id = ?', [newId]);
return !result.length ? newId : createNewId();
}
This returns the new ID, or it calls itself again.
Usage is either with .then():
createNewId().then(newId => {
console.log('found unused ID', newId);
// do something with it
}).catch(err => {
console.log('oops', err);
});
or inside an async function with await:
async function main() {
try {
const newId = await createNewId();
console.log('found unused ID', newId);
// do something with it
} catch (err) {
console.log('oops', err);
}
}

Related

how to fill an array outside the function clause in nodejs

I want to fill an array outside the function block
app.get('/getpackages/:dateStart/:dateEnd/:limit', function (req, res) {
var xlsSourceFilesRetrievedTsdz = []
var xlsSourceFilesRetrievedSvn = []
var dateStart = req.params.dateStart;
var dateEnd = req.params.dateStart;
var limit = Number(req.params.limit);
let sql = 'SELECT * FROM summary_dz WHERE Start != "" AND Start BETWEEN ? AND ? LIMIT ?'
db.query(sql, [dateStart,dateEnd,limit], function (err, results) {
if (err) throw err;
for (const counter in results) {
xlsSourceFilesRetrievedTsdz.push(results[counter].XlsSourceFile);
}
// console.log(xlsSourceFilesRetrievedTsdz)
});
console.log(xlsSourceFilesRetrievedTsdz)
I want to fill xlsSourceFilesRetrievedTsdz. Whats wrong with what i wrote? I get an emty array. The console.log inside the block in comment gives the wanted result How can the from outside the block?
Doing this should works:
app.get('/getpackages/:dateStart/:dateEnd/:limit', async (req, res) => {
var xlsSourceFilesRetrievedTsdz = []
var xlsSourceFilesRetrievedSvn = []
const promiseQuery = (sql, dateStart, dateEnd, limit) => {
return new Promise((res, rej)=> {
db.query(sql, [dateStart,dateEnd,limit], function (err, results) {
if (err) rej(err);
res(results)
});
})
}
const sql = 'SELECT * FROM summary_dz WHERE Start != "" AND Start BETWEEN ? AND ? LIMIT ?'
const dateStart = req.params.dateStart;
const dateEnd = req.params.dateStart;
const limit = Number(req.params.limit);
const results = await promiseQuery(sql, dateStart, dateEnd, limit)
for (const counter in results) {
xlsSourceFilesRetrievedTsdz.push(results[counter].XlsSourceFile);
}
console.log(xlsSourceFilesRetrievedTsdz)
}
What I do here was wrap the whole thing in a Promise and return it to wait te results.

How do I return an asynchronous DB query result from one module to another using Node.js?

I'm new to Node, and I'm trying to follow a pattern from a Udemy API course. The API is structured to utilize route, controller and service modules for flow. Database queries are to be run as services and they are supposed to be called from controllers.
I need to run a series of DB queries to generate a list (I'm showing only 2 of 6 queries in this example). I am running these using async/await in my function. The queries are working fine. My problem occurs when I try to return the 'batch result' (the result of all the queries) to the controller at the end of the process. I get Promise { <pending> }. I have tried many things, but I cannot end the promise to access the final result from my controller module--I can only access it from my service module.
Here is my code from my controller module (groups.controller.js) where I call my function:
const groupsService = require('../services/groups.service');
exports.propertyList = (req, res, next) => {
const uid = req.body.uid;
const batchResponse = groupsService.batchQuery(uid, res);
console.log(batchResponse);
}
And here is my code from my service module (groups.services.js) where I run the queries:
const mysql = require('mysql2');
const dbAsync = require("../config/db.config");
async function batchQuery(uid, res) {
var Q1;
var Q2;
var uid = uid * -1;
const pool = mysql.createPool(dbAsync.dbAsync);
const promisePool = pool.promise();
try {
Q1 = await promisePool.query('SELECT PropertyID FROM GroupMembership WHERE GroupID = ?', [uid]);
Q2 = await promisePool.query('SELECT SubGroupID FROM GroupMembership WHERE GroupID = ? AND PropertyID = ?', [uid, 0]);
}
catch(error) {
console.log(error);
res.status(401).send('Server error');
return error;
}
finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: '+ Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
res.status(200).send(batchResponse);
return batchResponse;
}
}
module.exports = {batchQuery};
When I send a post via postman, I get the expected query result (below). However, I can only get this to work if I put my res in my service module.
{
"Q1": [
{
"PropertyID": 0
}
],
"Q2": [
{
"SubGroupID": 397
}
]
}
Is there a way to end the promise in this pattern and return the desired batch response? Thank you.
EDIT: Adding the code updates provided by #traynor.
New controller:
const groupsService = require('../services/groups.service');
exports.propertyList = async (req, res, next) => {
const uid = req.body.uid;
let batchResponse;
try {
batchResponse = await groupsService.batchQuery(uid);
console.log(batchResponse);
return res.status(200).send(batchResponse);
} catch(error) {
console.log('Error: ' + error);
return res.status(401).send('Server error');
}
}
New service:
const mysql = require('mysql2');
const dbAsync = require("../config/db.config");
function batchQuery(uid) {
return new Promise((resolve, reject) => {
var Q1;
var Q2;
var uid = uid * -1;
const pool = mysql.createPool(dbAsync.dbAsync);
const promisePool = pool.promise();
try {
Q1 = await promisePool.query('SELECT PropertyID FROM GroupMembership WHERE GroupID = ?', [uid]);
Q2 = await promisePool.query('SELECT SubGroupID FROM GroupMembership WHERE GroupID = ? AND PropertyID = ?', [uid, 0]);
} catch(error) {
console.log(error);
reject(error);
} finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: '+ Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
resolve(batchResponse);
}
})
}
module.exports = {batchQuery};
the service is now returning a promise, and it's also handling response instead of controller.
to return from service, you need to promisify service: return a promise which resolves when you get db data, or on error, and then you also need to await the service, which it's wrapped in try/catch for error handling.
once it's all done, handle response from the controller:
service:
function batchQuery(uid) {
return new Promise(async (resolve, reject) => {
var Q1;
var Q2;
//...
try {
//...
} catch (error) {
console.log(error);
reject(error);
} finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: ' + Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
resolve(batchResponse);
}
});
controller:
exports.propertyList = async (req, res, next) => {
const uid = req.body.uid;
let batchResponse;
try {
batchResponse = await groupsService.batchQuery(uid);
console.log(batchResponse);
res.status(200).send(batchResponse);
} catch(error) {
return res.status(401).send('Server error');
}
}

Node loop insert with mySQL and Mongodb

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.

Extend variables outside MySQL query function in NodeJS

I tried to run a function which returns a value back but am getting undefined.
function getMessageId(myId, user){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+ myId+"')";
connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
return sql.id;
})
}
// running the function
msgId = getMessageId(userFrom, userTo);
console.log(msgId);
Now when I tried to console.log the sql I get the expected result like
{
id : 3,
userFrom : 3,
userTo : 1,
type : "normal",
date : "2017-06-25 06:56:34",
deleted : 0
}
But when I console.log the msgId I get undefined. I am doing this on NodeJS, please any better solution?
Short answer, Because its an asynchronous operation.
The outer console.log happens before the getMessageId returns.
If using callbacks, You can rewrite getMessageId as
let msgId
function getMessageId(myId, user, callback){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+ myId+"')";
return connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
callback(sql.id);
})
}
function setMsgId(id) {
msgId = id;
}
And then call it as,
getMessageId(userFrom, userTo, setMsgId);
Further I would suggest you look into Promises.
Which would very well streamline the flow.
Using Promises, getMessageId should look something like
function getMessageId(myId, user){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND
userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+
myId+"')";
const promise = new Promise((resolve, reject) => {
connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
resolve(sql.id);
})
return promise.
}
Post this, You can use it as
getMessageId(myId, user).then((msgId) => console.log(msgId))
create a wrapper for mysql use
// service/database/mysql.js
const mysql = require('mysql');
const pool = mysql.createPool({
host : 'host',
user : 'user',
password : 'pass',
database : 'dbname'
});
const query = (sql) => {
return new Promise((resolve, reject) => {
pool.query(sql, function(error, results, fields) {
if (error) {
console.error(error.sqlMessage);
return reject(new Error(error));
}
resolve(results);
});
});
}
module.exports = { query };
then call from another script with async funcion and await
// another file, with express route example
const db = require('/service/database/mysql.js')
module.exports = async (req, res) => { // <-- using async!
let output = []; // <-- your outside variable
const sql = 'SELECT * FROM companies LIMIT 10';
await db.query(sql) // <-- using await!
.then(function(result) {
output = result; // <-- push or set outside variable value
})
.catch(e => {
console.log(e);
});
// await db.query("next query" ...
// await db.query("next query" ...
// await db.query("next query" ...
res.json(output);
}
This is probably NOT a proper way, and a hack, but sharing for information purpose (you may not like to use this)
Simply use an if else and call the function once inside the query (if true), and call it outside (if false)
if (id != null) {
// Get Details
const query = pool.execute('SELECT * FROM `sometable` WHERE `id` = ?', [id], function(err, row) {
if (err) {
console.log(err)
return
} else {
if (row && row.length) {
// Do Something
}
}
})
} else {
// Do Something
}

ID being passed in as a parameter, query failing to return a row that actually exists

I'm using node-mysql trying to query the familiar Northwind database which I have locally. Here is my Customer model:
models/customer.js
var connection = require('../../database/connection');
var Customer = {
list: function(cb) {
var q = 'select CustomerID, ContactName from Customers limit 10';
return connection.query(q, cb);
},
read: function(id, cb) {
var q = 'select * from Customers where CustomerID = ' + id;
return connection.query(q, cb);
}
};
module.exports = Customer;
Alright, I know those kinds of queries are vulnerable to SQL injection but I'm coming from MongoDB and right now I'm experimenting to see that I can get my MySQL connections and queries to work before I refactor them for security. So here's how I call them from a controller:
controllers/customers.js
var Customer = require('../models/customer');
var CustomersController = {
index: function(req, res, next) {
var data = {};
Customer.list(function(err, rows) {
if (err) {
next(new Error('No customers found!'));
return;
}
data.title = 'All customers';
data.customers = rows;
res.render('customers/index', data);
});
},
view: function(req, res, next) {
var id = req.params.id;
var data = {};
Customer.read(id, function(err, row) {
if (err) {
next(new Error('The customer with ID ' + id + ' does not exist!'));
return;
}
data.title = row.ContactName;
data.customer = row;
res.render('customers/read', data);
});
}
};
module.exports = CustomersController;
Finally, the routes:
var router = require('express').Router();
var CustomersController = require('../../app/controllers/customers');
router.route('/customers')
.get(CustomersController.index)
router.route('/customers/:id')
.get(CustomersController.view)
module.exports = router;
GET /customers is working but GET /customers/:id does not. For example, if I want to view the details for the customer with id ALFKI, I get the error I threw in the controller -- "The customer with ID ALFKI does not exist!" yet when I directly query the database with the same query I used in my model, i.e select * from Customers where CustomerID = 'ALFKI', a row is returned.
Perhaps a fresh pair of eyes can tell me what I'm doing wrong?