MySQL Node.js Syntax Error (DRIVING ME CRAZY) - mysql

I'm getting an error that says:
C:\Users\Minh Lu\Desktop\MusicMediaWebApp\database\dbService.js:34
[0] con.query(sql, typeCast: function(field, next) {
[0] ^^^^^^^^
[0]
[0] SyntaxError: missing ) after argument list
From this:
/* Retrieves a User model by ID */
getUserByID: function(ID, callback) {
this.tryConnect().getConnection(function(err, con) {
var sql = queries.getUserByID;
con.query(sql, typeCast: function(field, next) {
// We only want to cast bit fields that have a single-bit in them. If the field
// has more than one bit, then we cannot assume it is supposed to be a Boolean.
if ( ( field.type === "BIT" ) && ( field.length === 1 ) ) {
var bytes = field.buffer();
// A Buffer in Node represents a collection of 8-bit unsigned integers.
// Therefore, our single "bit field" comes back as the bits '0000 0001',
// which is equivalent to the number 1.
return( bytes[ 0 ] === 1 );
}
return next();
}, ID, function (err, result) {
if (err) throw err;
// Call the callback function in the caller of this method so we can do something with this "result"
return callback(result); // [] if not found
});
});
},
And I'm so confused as to what syntax error this is? This is the same method as in the documentation: https://github.com/mysqljs/mysql#type-casting
Thanks!

Look closely at the documentation.
connection.query({
sql: '...',
typeCast: function (field, next) {
if (field.type == 'TINY' && field.length == 1) {
return (field.string() == '1'); // 1 = true, 0 = false
}
return next();
}
});
connection.query(...) is accepting an object as the parameter.

Related

Is there a way to pass a value from a mysql callback function to the outer function in express?

I'm using express and npm MySQL to develop an API.I have a json request in this format:
{
"payments":[
{
"PolicyNo": "ME3",
"PaymentDate": "2019-04-16T18:00:00.000Z",
},
{
"PolicyNo": "PIN001q",
"PaymentDate": "2019-04-16T18:00:00.000Z",
}]
}
I want to check the database if the policyNo exists before inserting. To avoid the common ERR_HTTP_HEADERS_SENT, I've looped through the payments querying the database with the PolicyNo. If it exists it's pushed into a success array if it doesn't it's pushed into a failed array.
This works perfectly but I can't access these arrays outside the callback.
Here's what I've tried:
router.post('/bla', (req, res)=>{
const values = []
const failedvalues = []
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?'
req.body.payments.forEach(element => {
connection.query(sql,element.PolicyNo,(err, rows) =>{
if(!err){
if(rows && rows.length > 0){
values.push(element.PolicyNo, element.PaymentDate)
}else{
failedvalues.push(element.PolicyNo)
}
}
})
})
res.json({
failed:failedvalues,
success:values
})
})
Here's the response I'm getting:
{
"failed": [],
"success": []
}
This has some major problems, mostly conceptually.
Firstly the forEach is synchronous will be called payments.length number of times, but the sql query is Asynchronous so it will complete in the future.
I think you are confused between synchronous and asynchronous functions and how they work.
But you can solve this (in your case) atleast two ways.
1) Use the IN syntax and get the array. Iterate over it and do stuff. "SELECT PolicyNo from pinclientinfo WHERE PolicyNo in (...)"
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo IN (' + Array(req.body.payments).fill('?').join(',') + ')'
const policies = req.body.payments.map(p => p.PolicyNo);
const values = [];
const failedvalues = [];
connection.query(sql, ...policies, (err, rows) => {
//now check each row..
rows.forEach(element => {
//Not optimized only depicts logic
///do stuff
/// like fill values and failedvalues
if(policies.indexOf(element.PolicyNo) > -1){
values.push(...)
}else{
failedvalues.push(...)
}
});
res.json({
failed: failedvalues,
success: values
})
})
Which will be 1 DB call.
2) The other approach is (not very good) doing multiple db calls and check for count.
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?'
let count = 0;
req.body.payments.forEach(element => {
connection.query(sql, element.PolicyNo, (err, rows) => {
if (!err) {
if (rows && rows.length > 0) {
values.push(element.PolicyNo, element.PaymentDate)
} else {
failedvalues.push(element.PolicyNo)
}
}
// check If all Complete
count+=1;
if(count === req.body.payments){
//all complete
res.json({
failed: failedvalues,
success: values
})
}
})
})
BUT SERIOUSLY, USE PROMISE. USE ASYNC/AWAIT USE THOSE SWEET LITTLE FEATURES ES6 GIVES YOU
Check out: this post
because connection.query is asynchronous, so return:
{
"failed": [],
"success": []
}
use promise and await you can synchronized resolve mysql data
use Promise.all() you can synchronized resolve list of promise
router.post("/bla", async (req, res) => {
let values = [];
let failedvalues;
let promises = [];
let sql = "SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?";
req.body.payments.forEach(element => {
promises.push(
new Promise(function(resolve, reject) {
connection.query(sql, element.PolicyNo, (err, rows) => {
if (!err) {
if (rows && rows.length > 0) {
values.push(element.PolicyNo, element.PaymentDate);
} else {
failedvalues.push(element.PolicyNo);
}
}
resolve();
});
})
);
});
await Promise.all(promises);
res.json({
failed: failedvalues,
success: values
});
});

Parsing json response gives undefined

I am using angularfire2 to fetch data from realtime DB. and response comes like below
{"type":"value",
"payload":{
"-LJXFAd_q3Cin64EBc7H":
{"_date":"9-8-2018",
"_deliveryType":"Pick up",
"_estDeliveryTime":"2018-08-10T11:43:57.164Z",
"_location":""}
}}
this on doing
the element inside payload is a key created using push. I wont know it to refer so how do i get the data under "LJXFAd_q3Cin64EBc7H" ?
there are many such entries inside payload and i need to fetch all.
code used to get above is:
getOrderHistory(uid:string){
console.log('start of getOrderHistory with uid:' + uid)
return new Promise((resolve, reject) =>
{
this.db.object("/users/" + uid + "/orders").snapshotChanges().subscribe(
res => {
//console.log('response:' + res)
resolve(res)
},
err => {
console.log(err)
reject(err)
}
)
})
}
Try this, it maps each snapshot to an object that holds its key and values.
this.db.object("/users/" + uid + "/orders").snapshotChanges()
.map(snapshot => {
const key = snapshot.key;
const data = snapshot.payload.val();
return { key, ...data };
})
.subscribe(res => {
resolve(res);
},
err => {
console.log(err);
reject(err);
});
normally, realtime database sends snapshot where you can do snapshot.id to get the id and .data (or .data() I forget at the moment) to get at the payload.
JSON.parse it then iterate it or get at it with dot notation or bracket notation. To get a property:
obj[payload][LJXFAd_q3Cin64EBc7H]
To iterate it:
function eachRecursive(obj)
{
for (var k in obj)
{
if (typeof obj[k] == "object" && obj[k] !== null)
eachRecursive(obj[k]);
else
// do something...
}
}

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?

Node.js sequential mysql queries promise not resolving

A route in app.js calls a function register(user) in a MySQL model, model.js. This register() calls displayNameTaken(display_name) which will return null if display name is available otherwise it will return a json object.
The promise in the app.post containing model.register(req.body) does not resolve.
If display name is taken register() will pass this json object back to the calling route.
If display name is not taken register() will register user and return back another json object back to the calling route.
The app never resolves the returned promise, app#113.
Or do you have any suggestions to what I should do instead?
Can you see what I have done wrong?
Output below:
1. When display name taken
app#113 [ undefined ]
model#73 { code: 12, message: 'e' }
2. Display name not taken, registration successful
app#113 [ undefined ]
model#73 undefined
model#61 110 //<- last insert id
The app never resolves the returned promise, app#113.
Or do you have any suggestions to what I should do instead?
Can you see what I have done wrong?
app.post('/api/register', function (req, res) {
Promise.all([
model.register(req.body)
]).then((r => {
console.log('app#113',r);// r=> undefined
res.status(200).json(r);
})).catch((e => {console.log(e);
res.status(500).json(e);
}));
});
function Model(db){
this.db = db;
}
//Function returns null if display name is not taken
Model.prototype.displayNameTaken = function(display_name){
return new Promise((resolve, reject, next) => {
var sql = "SELECT id FROM `users` WHERE `display_name` = ?";
var rv;
this.db.query(sql, [[display_name]], (err, result) => {
if (err) {
return resolve(err);
}
if(0 < result.length && result[0].id != undefined && result[0].id != NaN && 0 < result[0].id){
rv = {code: 12, message:'e'};
}else{
rv = null;
}
return resolve(rv);
});
});//Promise
}
model.register = function register(params){
if(params == undefined){
return;
}
var rv;
Promise.all([
this.displayNameTaken(params.display_name.trim())
]).then((r => {
return new Promise((resolve, reject, next) => {
if(r[0] == null){//display_name available
var sql = "INSERT INTO `users` (`display_name`, `email`, `hash`, `created`,`md51`, `md52`, `language`) VALUES ?";
var md51 = md5(randomString({length:32}));
var md52 = md5(randomString({length:32}));
var user = [[
params.display_name.trim(),
params.email.trim(),
passwordHash.generate(params.hash.trim()),
datetime.create().format('Y-m-d H:M:S'),
md51,
md52,
params.language
]];
this.db.query(sql, [user], function (err, result) {
if (err) {
return reject(err);
}
console.log('model#61',result.insertId);
if(0 < result.insertId){
rv = {code: 8, message:'i', md51: md51, md52: md52};
}else{
rv = {code: 0, message:'e'};
}
return resolve(rv);
});
}else{//display_name taken
rv = r[0];
}
console.log('model#73',rv);
return resolve(rv);
});//Promise
})).catch((e => {
console.log(e);
}));

How to return a nested json in node.js mysql in a single query

I'm trying to create an api that will return a nested json, coming from two related tables student and studentSubjects
[{
id:"1",
name: "John",
subjects: [{
id:"1",
subject: "Math"
},
{
id:"2",
subject: "English"
}
]
},
{
id:"2",
name: "Peter",
subjects: [{
id:"1",
subject: "Math"
},
{
id:"2",
subject: "English"
}
]
}]
My code looks like this:
this.get = function(res){
db.acquire(function(err, con){
con.query('SELECT * FROM students', function(err, results){
if (err){
res.send({status: 0, message: 'Database error'});
}else{
res.send({status: 1, data: results});
}
})
con.release()
})
}
I know the query should have joins, but it only returns single row. I tried also to make a loop, it won't work because its async
Thanks for your help!!
You cannot create a nested JSON from a MySQL query because it will always return a flat result.
Anyway, to create a nested JSON you should create multiple queries and insert the corresponding array object where needed.
You should really consider using Promises for creating nested queries because it will allow you to make asynchronous operations back to back.
Below code will also close the connection if an error occurs in any of the queries.
PS: I explained each step in the comments in the code below
Imagine having a database called 'School' and three tables called 'Student', 'Subject' and 'Link_student_subject'.
// Instantiate mysql datase variables
const mysql = require( 'mysql' );
const config = {
host : 'localhost',
user : 'root',
password : 'root',
database : 'school'
}
var connection;
// Instantiate express routing variables
const express = require('express');
const router = express.Router();
module.exports = router;
// Wrapper class for MySQL client
// - Constructor creates MySQL connection
// - Connection opened when query is done
// - Promise is resolved when executing
// - Promise returns reject in case of error
// - If promise resolved rows will be the result
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();
} );
} );
}
}
// Function that will execute a query
// - In case of an error: ensure connection is always closed
// - In case of succes: return result and close connection afterwards
Database.execute = function( config, callback ) {
const database = new Database( config );
return callback( database ).then(
result => database.close().then( () => result ),
err => database.close().then( () => { throw err; } )
);
};
// Instantiate Database
var database = new Database(config);
// Express routing
router.get('/students', function (req, res) {
// Variables - Rows from Students & Subjects & Link_student_subject
let rows_Students, rows_Subjects, rows_Link_Student_Subject;
// Create a Promise chain by
// executing two or more asynchronous operations back to back,
// where each subsequent operation starts when the previous operation succeeds,
// with the result from the previous step
Database.execute( config,
database => database.query( "select a.*, null as subjects from student a" )
.then( rows => {
rows_Students = rows;
return database.query( "select * from subject" )
} )
.then( rows => {
rows_Subjects = rows;
return database.query( "select * from link_student_subject" )
} )
.then( rows => {
rows_Link_Student_Subject = rows;
} )
).then( () => {
// Create your nested student JSON by looping on Students
// and inserting the corresponding Subjects array
for (let i = 0; i < rows_Students.length; i++) {
let arraySubjects = [];
for (let x = 0; x < rows_Link_Student_Subject.length; x++) {
if(rows_Students[i].id == rows_Link_Student_Subject[x].id_student){
arraySubjects.push(searchObjInArray(rows_Subjects, "id", rows_Link_Student_Subject[x].id_subject));
}
}
rows_Students[i].subjects = arraySubjects;
}
res.send(JSON.stringify(rows_Students));
} ).catch( err => {
// handle the error
res.send(err);
});
});
// Function - search if object in array has a value and return that object
function searchObjInArray(array, arrayProp, searchVal){
let result = null;
let obj = array.find((o, i) => {
if (o[arrayProp] == searchVal) {
result = array[i];
return true; // stop find function
}
});
return result;
}
If you run this code with node and go to "127.0.0.1/students" it will return exactly the same JSON as in your question.
All credits and extra info on MySQL and promises - https://codeburst.io/node-js-mysql-and-promises-4c3be599909b
MySQL 5.7+ has a JSON datatype that you can leverage for your "subjects" field. Here's a great tutorial on how to use that:
https://www.sitepoint.com/use-json-data-fields-mysql-databases/