Nodejs 12 Callback not working for mysql connection? - mysql

I'm writing this code to run in AWS Lambda.
Looking at mysql connection documentation
I expect, if things are working with out errors, to get the message "Database connected successfully!"
and then to get a message "connected as id " but that is not the order that it is happening. Here is my code.
'use strict';
let mysql = require('mysql');
const connection = mysql.createConnection({
dateStrings: true,
host : process.env.rds_host,
user : process.env.rds_user,
password : process.env.rds_password,
database : process.env.rds_database,
port : process.env.rds_port
});
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function(err) {
if (err) {
statusCode = 500;
responseBody = err;
}
else{
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
}
});
console.log('connected as id ' + connection.threadId);
connection.query(sql, data, function(queryError, results) {
if(queryError) {
console.error(queryError.message);
callback(queryError);
}
else
{
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
};
Here is the logged output:
INFO
GetCompanies SQL: SELECT * FROM company ORDER BY points DESC, name ASC 2020-01-01T11:52:57.813Z
INFO connected as id null 2020-01-01T11:52:57.952Z
INFO Database connected successfully! 2020-01-01T11:52:57.974Z
My thought was that the function that I supply to:
connection.connect(function(err) {
would execute before any code after connection.connect. Am I wrong to think that?
One more question: Why is the
connected as id null? I got that code (connection.threadId) straight from the mysql docs.
I know this is no big deal if all it effects is the order of log messages but I have other functions where I can't make queries because the connection isn't there yet.
It's quite confusing when it blows right past the
if (err) {
statusCode = 500;
responseBody = err;
}
else{
<do some connection.query code here>
}
I put these log messages here because this method works and my other method doesn't.

You are getting that behaviour because console.log('connected as id ' + connection.threadId); is not waiting for connection.connect to finish connecting.
You need to use promises here.
You could try to use async/await.
exports.handler = async (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
try {
await connection.connect();
console.log("Database connected successfully!");
console.log('connected as id ' + connection.threadId);
const queryResult = await connection.query(sql, data);
// do something with queryResult
} catch (e) {
// handle error
}
};
EDIT
The resource you linked to suggests to use promises over callbacks:
await any promise instead of using callbacks

would execute before any code after connection.connect. Am I wrong to think that?
The function you are passing to the connection.connect is called a
callback function. what that mean is, it will be called only after a
successful connection(or after the connection attempt is errored) is made. So the answer is no. The connection.connect is called and then immediately it will call the next statement. It will not wait for the current statement, because javascript is event driven and non blocking.
connected as id null? I got that code (connection.threadId) straight from the mysql docs.
This is because of the previous statement, you are logging the connectionId before a connection is obtained.
using Callbacks
Your code should be like the below. I haven't modified much, the connection to the database is made only after the callback of the connect event is called.
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function (err) {
// we are inside the callback function a successful connection has been obtained , or error connecting to database
if (err) {
statusCode = 500;
responseBody = err;
}
else {
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
}
// connection exists
console.log('connected as id ' + connection.threadId);
connection.query(sql, data, function (queryError, results) {
if (queryError) {
console.error(queryError.message);
callback(queryError);
}
else {
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
});
};
using promises
If you could use promises to make your code readable and understandable.
import { promisify } from 'util';
exports.handler = async (event, context) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
const connect = promisify(connection.connect);
const query = promisify(connection.query);
try {
const connection = await connect();
// connection exists
console.log('connected as id ' + connection.threadId);
const results= await query(sql);
return {
statusCode: 200,
responseBody: JSON.stringify(results)
}
} catch (err) {
return {
statusCode: 500,
responseBody: err.message
}
}
};

You should move your connection.query statement into the connection.connect scope as even if you can not connect to database you will try to make a query which is not working properly.
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function(err) {
if (err) {
statusCode = 500;
responseBody = err;
}
else{
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
connection.query(sql, data, function(queryError, results) {
if(queryError) {
console.error(queryError.message);
callback(queryError);
}
else
{
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
}
});
console.log('connected as id ' + connection.threadId);
};

Related

Node.js executing mysql query after receiving message from mqtt broker

I have a node.js file that subscribes to a topic and upon receiving a published message scans a local mysql db for the most recent entry in a variable named "command". Command values will trigger various responses, but I have left this portion out since my issue is before this.
My mysql query appears to be giving me errors. I am trying to look for the most recent entry of the command column and assign the value to a var command. I thought this code would do the trick:
var sql = 'SELECT command FROM motoron2 ORDER BY id DESC LIMIT 1';
con.query(sql, function (err, result) {
if (err) throw err;
});
console.log(result);
var command = result[1];
console.log(command);
But I am getting the following response which seems to indicate an error in the mysql query:
user#server.domain [bin]# node motorlistener.js
Connected to MYSQL!
Connected to Broker!
{"pulse":1}
1
/home/user/etc/domain/bin/motorlistener.js:62
console.log(result);
^
ReferenceError: result is not defined
at MqttClient.<anonymous> (/home/user/etc/domain/bin/motorlistener.js:62:17)
at MqttClient.emit (events.js:314:20)
at MqttClient._handlePublish (/home/user/node_modules/mqtt/lib/client.js:1277:12)
at MqttClient._handlePacket (/home/user/node_modules/mqtt/lib/client.js:410:12)
at work (/home/user/node_modules/mqtt/lib/client.js:321:12)
at Writable.writable._write (/home/user/node_modules/mqtt/lib/client.js:335:5)
at doWrite (/home/user/node_modules/readable-stream/lib/_stream_writable.js:409:139)
at writeOrBuffer (/home/user/node_modules/readable-stream/lib/_stream_writable.js:398:5)
at Writable.write (/home/user/node_modules/readable-stream/lib/_stream_writable.js:307:11)
at TLSSocket.ondata (_stream_readable.js:718:22)
The full code is below, but does anyone know what is causing this error?
////////////////////////////////////////////////////////////////////////////////
//setup
var mqtt = require('mqtt'); //for client use
const fs = require('fs');
var caFile = fs.readFileSync("/home/user/etc/domain/bin/ca.crt");
var topic = "heartbeat";
var mysql = require('mysql');
var con = mysql.createConnection({
host : 'localhost',
user : 'myuser',
password : 'mypass',
database : 'mydb'
});
var options={
port:8883,
clientId:"yo",
username:"myuser2",
password:"mypassw",
protocol: 'mqtts',
clean:true,
rejectUnauthorized: false,
retain:false,
ca:caFile
};
var client = mqtt.connect("http://dns.org",options);
//mqtt connection dialog
client.on("connect",function(){
console.log("Connected to Broker!");
client.subscribe(topic, {qos:1});
});
//mqtt connection handle errors
client.on("error",function(error){
console.log("Broker Connection Error");
process.exit(1);
});
//database connection
con.connect(function(err) {
if (err) throw err;
console.log("Connected to MYSQL!");
});
//handle incoming messages from broker
client.on('message',function(topic, message, packet){
var raw = ""+message;
console.log(raw);
var obj = JSON.parse(raw);
var pulse = obj.pulse;
console.log(pulse);
var sql = 'SELECT command FROM motoron2 ORDER BY id DESC LIMIT 1';
con.query(sql, function (err, result) {
if (err) throw err;
});
console.log(result);
var command = result[1];
console.log(command);
if (command == 1) {
console.log("command=1");
}
else {
console.log("command not equal to 0");
}
});
I am getting the following response which seems to indicate an error in the mysql query
That's not an error in your MySQL query. It's a null reference error because you're trying to use result outside the callback.
Changing your code to this will work:
var sql = 'SELECT command FROM motoron2 ORDER BY id DESC LIMIT 1';
con.query(sql, function (err, result) {
if (err) {
throw err;
}
// access result inside the callback
console.log(result);
var command = result[0];
console.log(command);
});
Depending on your environment you may be able to re-write your code using promises and async/await to reduce the nested scopes.
To do so, you'd need to turn your callback into a promise and then you can await it, like so:
let sql = 'SELECT command FROM motoron2 ORDER BY id DESC LIMIT 1';
// 1 -- we turn the query into a promise
const queryPromise = new Promise((resolve, reject) => {
con.query(sql, function (queryError, queryResult) {
if (queryError) {
reject(queryError);
}
resolve(queryResult);
});
});
try {
// 2 -- we can now await the promise; note the await
let result = await queryPromise;
// 3 -- now we can use the result as if it executed synchronously
console.log(result);
let command = result[0];
console.log(command);
} catch(err) {
// we can catch query errors and handle them here
}
Putting it all together, you should be able to change the on message event handler to an async function in order to take advantage of the async/await pattern as shown above:
client.on('message', async function(topic, message, packet) {
/* .. you can use await here .. */
});
All above code from #Mike Dinescu works perfectly fine. Just dont forget on the end to close the connection!
Else the runner will hangs after tests have finished.
the full solution:
async function mySqlConnect(dbquery) {
const conn = mysql.createPool({
host: 'localhost',
port: 3306,
user: 'test',
password: 'test',
database: 'test'
}, { debug: true });
// 1 -- we turn the query into a promise
const queryPromise = new Promise((resolve, reject) => {
conn.query(dbquery, function (queryError, queryResult) {
if (queryError) {
reject(queryError);
}
resolve(queryResult);
});
});
try {
// 2 -- we can now await the promise; note the await
let result = await queryPromise;
// 3 -- now we can use the result as if it executed synchronously
//console.log(result);
let command = await result[0];
//console.log(command);
return command;
} catch(err) {
}
finally{
conn.end(function(err) {
if (err) {
return console.log('error:' + err.message);
}
//console.log('Close the database connection.');
});
}
}

convert a NodeJS lambda function (AWS) to use "async" (promises, etc.) instead of callbacks

I have a lambda function that connects to mysql and runs a set of queries, but I actually have a sequence of mysql queries that need to run one after another. I.e., the value of one query is used in the next query, etc.
Currently, I have a bunch of callbacks to achieve this, but this is leading to "callback hell". How would I rewrite this to use async / await?
My code is actually split into 2 files. The first file does an initial query, and then the value is passed into a function of the second file. Please note that the mysql node_module is included but not shown here. The AWS API gateway calls index.js
// index.js
var mysql = require('mysql'); // from node_modules
var config = require('./config.json');
var dashboard = require('./dashboard.js');
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
// check for mysql connection error first
if ( err ) {
throw err;
}
let qry = "select id from some_table where some_field = ?";
let someval = event.queryStringParameters.someval;
connection.query(qry, [someval], function(error, result) {
if ( error ) {
throw err;
}
else {
dashboard.processRequest(connection, callback, event, res[0].id);
}
});
});
}
// dashboard.js
module.exports = {
jsonResponse: function(results) {
return {
"statusCode": 200,
"body": JSON.stringify({ results }),
"isBase64Encoded": false,
"headers": {
"Access-Control-Allow-Origin": "*"
}
};
},
processRequest: function(connection, callback, event, val) {
let qry = "update first_table set some_field = ?";
connection.query(qry, [val], function(error, results) {
// return to client if error
if (error) {
callback(null, this.jsonResponse(error));
}
else {
// assume that this table must be update AFTER the previous statement
qry = "select id from second_table where some_field = ?";
connection.query(qry, [val], function(error1, results1) {
// return to client if error
if ( error1 ) {
callback(null, this.jsonResponse(error1));
}
qry = "update third_table set some_field = ? where id = ?";
connection.query(qry, [results1[0].id], function(error2, results2) {
// release connection when all queries are completed
connection.release();
if ( error2 ) {
callback(null, this.jsonResponse(error2));
}
else {
callback(null, this.jsonResponse(results2));
}
});
});
}
});
}
};
It was suggested to me that something like the below code might work. Unfortunately, it does not. I was curious to know why using try...catch blocks in the way shown below is not working, and is it the same thing as what you've shown, but just written differently?
// index.js
var mysql = require('mysql'); // from node_modules
var config = require('./config.json');
var dashboard = require('./dashboard.js');
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
// check for mysql connection error first
if ( err ) {
throw err;
}
let qry = "select id from users where username = ? limit 1;";
let username = event.queryStringParameters.username;
try {
let res = await connection.query(qry, [event.queryStringParameters.username]);
dashboard.processRequest(connection, callback, event, res[0].id);
} catch (err) {
console.log(err);
}
});
}
// dashboard.js
module.exports = {
jsonResponse: function (results) {
return {
"statusCode": 200,
"body": JSON.stringify({results}),
"isBase64Encoded": false,
"headers": {
"Access-Control-Allow-Origin": "*"
}
};
},
processRequest: async function (connection, callback, event, val) {
let qry = "update first_table set some_field = ?";
try {
let results = await connection.query(qry, [val]);
qry = "select id from second_table where some_field = ?";
try {
let results1 = await connection.query(qry, [val]);
qry = "update third_table set some_field = ? where id = ?";
try {
let results2 = await connection.query(qry, [results1[0].id]);
connection.release();
callback(null, this.jsonResponse(results2));
} catch (error2) {
callback(null, this.jsonResponse(error2));
}
} catch (error1) {
callback(null, this.jsonResponse(error1));
}
} catch (error) {
callback(null, this.jsonResponse(error));
}
}
};
We need use promises.
Typically I follow this approach:
Create one async method mainProcess and have bunch of methods step by step called with in that method. one after the other with await or all at once.
Each async method getConnection and runQuery in this case, called within mainProcess must a Promise.
If any errors from these methods i.e promise rejects from individual methods, goes in catch block of mainProcess().
If no errors, all methods within mainProcess gets executed and goes to then block of mainProcess()
Your code can be refactored like this (just wrote in an editor untested)
var pool = mysql.createPool({
host: config.dbhost,
user: config.dbuser,
password: config.dbpassword,
database: config.dbname,
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
/**
* Main Lambda Process
*/
const mainProcess = async () => {
// Get Connection
let connection = await getConnection();
// Run Step 1
let qry1 = "select id from some_table1 where some_field = ?";
const response1 = await runQuery(connection, qry1, { someFiledValue: 1222})
// Run Step 2
let qry2 = "select id from some_table2 where some_field = ?";
const resonse2 = await runQuery(connection, qry2, { someFiledValue: 1222})
return 'All Good';
});
}
mainProcess()
.then(result => {
// All lambda success messages are returned from here
callback(null, result);
})
.catch(error => {
// All lambda errors thrown from here
callback(error);
});
};
function getConnection(qry, parms) {
return new Promise((resolve, reject) => {
pool.getConnection(function (error, connection) {
if (error) {
// return to client if error
reject(error);
} else {
// Return response
resolve(connection);
}
});
});
}
/**
* Runs a query, either resolves or rejects
*/
function runQuery(connection, qry, parms) {
return new Promise((resolve, reject) => {
connection.query(qry, [val], function (error, results) {
if (error) {
// return to client if error
reject(error);
} else {
// Return response
resolve(result);
}
});
});
}
When you're dealing with a lambda function which performs an async task you have two solutions:
you can use non async handlers, in which case you need to invoke "callback" on promises as you did in your example
you can use async handlers, which does not requires the "callback" input and that allows you to write async/await code, like the following example:
const mysql = require('mysql2/promise');
exports.handler = async(event, context) => {
//get path variable
const { pathVar } = event.pathParameters;
// get connection
const connection = await mysql.createConnection({
host : process.env.RDS_HOSTNAME,
user : process.env.RDS_USERNAME,
password : process.env.RDS_PASSWORD,
database : process.env.RDS_DB_NAME
});
// get text query
const textQuery = `SELECT field FROM entity WHERE attribute = ${pathVar}`;
// get res
const results = await connection.execute(textQuery);
return {
"statusCode": 200,
"body": results,
"isBase64Encoded": false
}
}
You can have a look at the AWS docs: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Nodejs mysql how to handle timeout and Handshake inactivity timeout

I am a newbie in Nodejs and I have a lambda function written on NodeJS that's supposed to delete some rows and insert some data on a mysql db.
I have come across various instances with error messages such as PROTOCOL_SEQUENCE_TIMEOUT, PROTOCOL_CONNECTION_LOST and an instance where the RDS db dns couldn't be resolved and connect to the db.
I was wondering how I might handle these events so I'd be able to re-connect and proceed.
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'somehost',
user : 'someuser',
password : 'somepassword',
database : 'somedb',
port : 3306
});
pool.on('connection', function (connection) {
console.log('Pool id %d connected', connection.threadId);
});
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
let request = JSON.parse(event.body);
/** SOME OTHER LOGIC HERE **/
let delete_query = "DELETE FROM answers WHERE sId= ? AND `key` = ?";
pool.query(delete_query, [sId, questionId], function(err, result){
if(err) throw err;
});
let insert_query = "INSERT INTO answers (qId, sId, `key`, value, hutk) VALUES ?";
pool.query(insert_query, [values], function(err, result){
if(err) throw err;
console.log("Successfull Insert")
});
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({message : 'success'}),
};
return response;
};
And also am I using the best approach to connecting to the db as in a pool or should I be using just a connection etc?
You can cache the connection, so the first call to your lambda would create the connection and the 2nd call (if the lambda is not cold started) can reuse the connection and is much faster.
Here is how we do it:
const mysql = require('mysql');
const util = require('util');
let mySQLconnection = undefined;
exports.handler = async function handler(event, context) {
try {
getMySQLConnection();
const queryResult = await mySQLconnection.query('Select * from yourtable where id = ? and attribute = ?', [id, attribute]);
} catch (error) {
console.log('ERROR: ' + error);
}
};
function getMySQLConnection() {
if (mySQLconnection !== undefined) {
return;
}
mySQLconnection = mysql.createConnection(yourConnectionJson);
mySQLconnection.query = util.promisify(mySQLconnection.query);
}
You could also do a connection retry in the catch block.

Use promise to process MySQL return value in node.js

I have a python background and is currently migrating to node.js. I have problem adjusting to node.js due to its asynchronous nature.
For example, I am trying to return a value from a MySQL function.
function getLastRecord(name)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
}
else {
//console.log(rows);
return rows;
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
var rows = getLastRecord('name_record');
console.log(rows);
After some reading up, I realize the above code cannot work and I need to return a promise due to node.js's asynchronous nature. I cannot write node.js code like python. How do I convert getLastRecord() to return a promise and how do I handle the returned value?
In fact, what I want to do is something like this;
if (getLastRecord() > 20)
{
console.log("action");
}
How can this be done in node.js in a readable way?
I would like to see how promises can be implemented in this case using bluebird.
This is gonna be a little scattered, forgive me.
First, assuming this code uses the mysql driver API correctly, here's one way you could wrap it to work with a native promise:
function getLastRecord(name)
{
return new Promise(function(resolve, reject) {
// The Promise constructor should catch any errors thrown on
// this tick. Alternately, try/catch and reject(err) on catch.
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
connection.query(query_str, query_var, function (err, rows, fields) {
// Call reject on error states,
// call resolve with results
if (err) {
return reject(err);
}
resolve(rows);
});
});
}
getLastRecord('name_record').then(function(rows) {
// now you have your rows, you can see if there are <20 of them
}).catch((err) => setImmediate(() => { throw err; })); // Throw async to escape the promise chain
So one thing: You still have callbacks. Callbacks are just functions that you hand to something to call at some point in the future with arguments of its choosing. So the function arguments in xs.map(fn), the (err, result) functions seen in node and the promise result and error handlers are all callbacks. This is somewhat confused by people referring to a specific kind of callback as "callbacks," the ones of (err, result) used in node core in what's called "continuation-passing style", sometimes called "nodebacks" by people that don't really like them.
For now, at least (async/await is coming eventually), you're pretty much stuck with callbacks, regardless of whether you adopt promises or not.
Also, I'll note that promises aren't immediately, obviously helpful here, as you still have a callback. Promises only really shine when you combine them with Promise.all and promise accumulators a la Array.prototype.reduce. But they do shine sometimes, and they are worth learning.
I have modified your code to use Q(NPM module) promises.
I Assumed your 'getLastRecord()' function that you specified in above snippet works correctly.
You can refer following link to get hold of Q module
Click here : Q documentation
var q = require('q');
function getLastRecord(name)
{
var deferred = q.defer(); // Use Q
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
deferred.reject(err);
}
else {
//console.log(rows);
deferred.resolve(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
return deferred.promise;
}
// Call the method like this
getLastRecord('name_record')
.then(function(rows){
// This function get called, when success
console.log(rows);
},function(error){
// This function get called, when error
console.log(error);
});
I am new to Node.js and promises. I was searching for a while for something that will meet my needs and this is what I ended up using after combining several examples I found. I wanted the ability to acquire connection per query and release it right after the query finishes (querySql), or to get a connection from pool and use it within Promise.using scope, or release it whenever I would like it (getSqlConnection).
Using this method you can concat several queries one after another without nesting them.
db.js
var mysql = require('mysql');
var Promise = require("bluebird");
Promise.promisifyAll(mysql);
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);
var pool = mysql.createPool({
host: 'my_aws_host',
port: '3306',
user: 'my_user',
password: 'my_password',
database: 'db_name'
});
function getSqlConnection() {
return pool.getConnectionAsync().disposer(function (connection) {
console.log("Releasing connection back to pool")
connection.release();
});
}
function querySql (query, params) {
return Promise.using(getSqlConnection(), function (connection) {
console.log("Got connection from pool");
if (typeof params !== 'undefined'){
return connection.queryAsync(query, params);
} else {
return connection.queryAsync(query);
}
});
};
module.exports = {
getSqlConnection : getSqlConnection,
querySql : querySql
};
usage_route.js
var express = require('express');
var router = express.Router();
var dateFormat = require('dateformat');
var db = require('../my_modules/db');
var getSqlConnection = db.getSqlConnection;
var querySql = db.querySql;
var Promise = require("bluebird");
function retrieveUser(token) {
var userQuery = "select id, email from users where token = ?";
return querySql(userQuery, [token])
.then(function(rows){
if (rows.length == 0) {
return Promise.reject("did not find user");
}
var user = rows[0];
return user;
});
}
router.post('/', function (req, res, next) {
Promise.resolve().then(function () {
return retrieveUser(req.body.token);
})
.then(function (user){
email = user.email;
res.status(200).json({ "code": 0, "message": "success", "email": email});
})
.catch(function (err) {
console.error("got error: " + err);
if (err instanceof Error) {
res.status(400).send("General error");
} else {
res.status(200).json({ "code": 1000, "message": err });
}
});
});
module.exports = router;
I am still a bit new to node, so maybe I missed something let me know how it works out. Instead of triggering async node just forces it on you, so you have to think ahead and plan it.
const mysql = require('mysql');
const db = mysql.createConnection({
host: 'localhost',
user: 'user', password: 'password',
database: 'database',
});
db.connect((err) => {
// you should probably add reject instead of throwing error
// reject(new Error());
if(err){throw err;}
console.log('Mysql: Connected');
});
db.promise = (sql) => {
return new Promise((resolve, reject) => {
db.query(sql, (err, result) => {
if(err){reject(new Error());}
else{resolve(result);}
});
});
};
Here I am using the mysql module like normal, but instead I created a new function to handle the promise ahead of time, by adding it to the db const. (you see this as "connection" in a lot of node examples.
Now lets call a mysql query using the promise.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
What I have found this useful for is when you need to do a second query based on the first query.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
var sql = "SELECT * FROM friends WHERE username='";
sql = result[0];
sql = "';"
return db.promise(sql);
}).then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
You should actually use the mysql variables, but this should at least give you an example of using promises with mysql module.
Also with above you can still continue to use the db.query the normal way anytime within these promises, they just work like normal.
Hope this helps with the triangle of death.
You don't need to use promises, you can use a callback function, something like that:
function getLastRecord(name, next)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
next(err);
}
else {
//console.log(rows);
next(null, rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
getLastRecord('name_record', function(err, data) {
if(err) {
// handle the error
} else {
// handle your data
}
});
Using the package promise-mysql the logic would be to chain promises using then(function(response){your code})
and
catch(function(response){your code}) to catch errors from the "then" blocks preceeding the catch block.
Following this logic, you will pass query results in objects or arrays using return at the end of the block. The return will help passing the query results to the next block. Then, the result will be found in the function argument (here it is test1). Using this logic you can chain several MySql queries and the code that is required to manipulate the result and do whatever you want.
the Connection object is created to be global because every object and variable created in every block are only local. Don't forget that you can chain more "then" blocks.
var config = {
host : 'host',
user : 'user',
password : 'pass',
database : 'database',
};
var mysql = require('promise-mysql');
var connection;
let thename =""; // which can also be an argument if you embed this code in a function
mysql.createConnection(config
).then(function(conn){
connection = conn;
let test = connection.query('select name from records WHERE name=? LIMIT 1',[thename]);
return test;
}).then(function(test1){
console.log("test1"+JSON.stringify(test1)); // result of previous block
var result = connection.query('select * from users'); // A second query if you want
connection.end();
connection = {};
return result;
}).catch(function(error){
if (connection && connection.end) connection.end();
//logs out the error from the previous block (if there is any issue add a second catch behind this one)
console.log(error);
});
To answer your initial question: How can this be done in node.js in a readable way?
There is a library called co, which gives you the possibility to write async code in a synchronous workflow. Just have a look and npm install co.
The problem you face very often with that approach, is, that you do not get Promise back from all the libraries you like to use. So you have either wrap it yourself (see answer from #Joshua Holbrook) or look for a wrapper (for example: npm install mysql-promise)
(Btw: its on the roadmap for ES7 to have native support for this type of workflow with the keywords async await, but its not yet in node: node feature list.)
This can be achieved quite simply, for example with bluebird, as you asked:
var Promise = require('bluebird');
function getLastRecord(name)
{
return new Promise(function(resolve, reject){
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
reject(err);
}
else {
resolve(rows);
//console.log(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
});
}
getLastRecord('name_record')
.then(function(rows){
if (rows > 20) {
console.log("action");
}
})
.error(function(e){console.log("Error handler " + e)})
.catch(function(e){console.log("Catch handler " + e)});
May be helpful for others, extending #Dillon Burnett answer
Using async/await and params
db.promise = (sql, params) => {
return new Promise((resolve, reject) => {
db.query(sql,params, (err, result) => {
if(err){reject(new Error());}
else{resolve(result);}
});
});
};
module.exports = db;
async connection(){
const result = await db.promise("SELECT * FROM users WHERE username=?",[username]);
return result;
}

Node js call function, that access mysql database and returns json result, multiple times

I'm new to Node.js. I have a function 'getFromDb' that accesses a mysql database and returns a json file with some data. What if I have an array of query data and I want to call the same function through a for loop to get a json file for each element of the array?
var http = require('http');
http.createServer(function(req, res) {
console.log('Receving request...');
var callback = function(err, result) {
res.setHeader('Content-disposition', 'attachment; filename=' + queryData+ '.json');
res.writeHead(200, {
'Content-Type' : 'x-application/json'
});
console.log('json:', result);
res.end(result);
};
getFromDb(callback, queryData);}
).listen(9999);
function getFromDb(callback, queryData){
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'xxxx',
password : 'xxxx',
database : 'xxxx',
port: 3306
});
connection.connect();
var json = '';
var data = queryData + '%';
var query = 'SELECT * FROM TABLE WHERE POSTCODE LIKE "' + data + '"';
connection.query(query, function(err, results, fields) {
if (err)
return callback(err, null);
console.log('The query-result is: ', results);
// wrap result-set as json
json = JSON.stringify(results);
/***************
* Correction 2: Nest the callback correctly!
***************/
connection.end();
console.log('JSON-result:', json);
callback(null, json);
});
}
You could use the async library for node for this. That library has many functions that make asynchronous programming in NodeJS much easier. The "each" or "eachSeries" functions would work. "each" would make all the calls to mysql at once time, while "eachSeries" would wait for the previous call to finish. You could use that inside your getFromDB method for your array.
See:
https://github.com/caolan/async#each
var http = require('http'),
async = require('async');
http.createServer(function(req, res) {
console.log('Receving request...');
var callback = function(err, result) {
res.setHeader('Content-disposition', 'attachment; filename=' + queryData+ '.json');
res.writeHead(200, {
'Content-Type' : 'x-application/json'
});
console.log('json:', result);
res.end(result);
};
getFromDb(callback, queryData);}
).listen(9999);
function getFromDb(callback, queryData){
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'xxxx',
password : 'xxxx',
database : 'xxxx',
port: 3306
});
connection.connect();
var arrayOfQueryData = ["query1", "query2", "query3", "query4", "query5"];
var jsonResults = [];
async.each(arrayOfQueryData, function (queryData, cb) {
var data = queryData + '%';
var query = 'SELECT * FROM TABLE WHERE POSTCODE LIKE "' + data + '"';
connection.query(query, function(err, results, fields) {
if (err)
return cb(err);
console.log('The query-result is: ', results);
// wrap result-set as json
var json = JSON.stringify(results);
console.log('JSON-result:', json);
jsonResults.push(json);
cb();
});
}, function (err) {
connection.end();
// callbacks from getFromDb
if (err) {
callback(err);
}
else {
callback(null,jsonResults);
}
});
}
use async module. it is the best one. If u dont want to add new module try following;
var count = 0;
array.forEach(function(element) { //array of the data that is to be used to call mysql
++count; //increase counter for each service call
async.db.call(element, callback); //the async task
}
var data = [];
function callback(err, resp) {
--count;//subtract for each completion
data.push(resp)
if(count == 0) { //return data when all is complete
return data;
}
}
I would recommend the async module though. it is very good practice and useful.