Lambda function MySQL result not working on NodeJs 8.10 - mysql

I have a code in Node 6.10 and it is working...
But If I convert it to Node 8.10 it's not working
var mysql = require("mysql");
var connection = mysql.createConnection({
host: " localhost",
user: "root",
password: "",
database: "parser_db"
});
exports.handler = async event => {
connection.connect();
let response = {
statusCode: 400,
body: { Method: "Invalid", event }
};
var readTable = "SELECT * FROM documents where id = " + mysql.escape(1);
connection.query(readTable, function(err, results, fields) {
if (err) throw err;
else {
response = {
statusCode: 200,
body: { results }
//body: { results }
};
console.log(response);
return response;
}
});
};
Can some one please help me to detect the problem. It is also not working if I do the MySQL query in separate file and return the result set.
Note : If I print the result using console.log(response) instead returning it's
showing the correct result.

The problem is that you are returning response from within the connection.query() callback function. That makes response the return value for the callback function, not the return value for the outer Lambda function.
One way to restructure this code is as follows:
exports.handler = async (event) => {
connection.connect();
return new Promise((resolve, reject) => {
const readTable = `SELECT * FROM documents where id = ${mysql.escape(1)}`;
connection.query(readTable, (err, results, fields) => {
if (err) {
reject(err);
} else {
resolve({statusCode: 200, body: {results}});
}
});
});
};

In addition to #jarmod's answer, You can also use the util.promisify method to promisify connection.query so that you can use the await keyword, to make the code simpler
const util = require('util');
exports.handler = async (event) => {
connection.connect();
const readTable = `SELECT * FROM documents where id = ${mysql.escape(1)}`;
const connQueryPromisified = util.promisify(connection.query).bind(connection);
const result = await connQueryPromisified(readTable);
return {statusCode: 200, body: {results}};
};

Related

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

How can I resolve a promised mysql query in express.js?

I'm trying to use the npm package promise-mysql and return json data (or a string doesn't matter) but I'm having issues following the promise chain with await/async.
With the current code i'm receiving Promise { undefined } in the console.log I have right before the response to the user. The response just sends nothing to the user and closes it. Can anyone point in the right direction of how to debug this?
index.js
app.get("/", async (req, res) => {
console.log( Promise.resolve(await getLogs()) )
res.send(await getLogs());
});
mysql.js
const mysql = require("promise-mysql");
let pool;
async function startDatabasePool() {
pool = await mysql.createPool({
connectionLimit: 10,
host: "xxx",
user: "xxx",
password: "xxx",
database: "xxx"
});
}
async function getDatabasePool() {
if (!pool) await startDatabasePool();
return pool;
}
module.exports = {
getDatabasePool,
startDatabasePool
};
users.js
const { getDatabasePool } = require("./mysql");
async function getLogs() {
let pool = await getDatabasePool();
pool.query("SELECT * from logs order by logdate desc", function(
error,
results,
fields
) {
if (error) throw error;
return JSON.stringify(results);
});
}
module.exports = {
getLogs
};
index.js
app.get("/", async (req, res) => {
const result = await getLogs();
res.send(result);
});
mysql.js
const mysql = require("promise-mysql");
let pool;
module.exports.startDatabasePool = async () => {
pool = await mysql.createPool({
connectionLimit: 10,
host: "xxx",
user: "xxx",
password: "xxx",
database: "xxx"
});
}
module.exports.getDatabasePool = async () => {
if (!pool) await startDatabasePool();
return pool;
}
// convert function as promise
module.exports.executeQuery = async(params) => {
return new Promise((resolve, reject) => {
pool.query(params, function (error, result, fields) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
users.js
const { executeQuery } = require("./mysql");
module.exports.getLogs = async () => {
return await executeQuery("SELECT * from logs order by logdate desc");
}
First I'd try it like:
app.get("/", async (req, res) => {
let logs = await getLogs()
console.log(logs)
res.send(logs);
});
I hope it helps!

Undefined result - Getting data from phpmyadmin using dialogflow

I'm working on a project to retrieve data from phpmyadmin by using Dialogflow.
I've been following a tutorial on YouTube.
However, the results after performing a very simple query is undefined.
Here is my code
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const mysql = require('mysql');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function connectToDatabase(){
const connection = mysql.createConnection({
host: '***.***.***.***',
user: '*************',
password: '*********',
database: '****************'
});
return new Promise((resolve, reject) => {
connection.connect();
resolve(connection);
});
}
function queryDatabase(connection){
return new Promise((resolve, reject) => {
connection.query('SELECT * FROM Data', (error, results, fields) => {
resolve(results);
});
});
}
function handleReadDB(agent){
return connectToDatabase()
.then(connection => {
return queryDatabase(connection)
.then(result => {
console.log(result);
connection.end();
});
});
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('ReadDB', handleReadDB);
// intentMap.set('your intent name here', googleAssistantHandler);
agent.handleRequest(intentMap);
});
the results show something like this
3:10:29.505 PM
outlined_flag
dialogflowFirebaseFulfillment
Function execution took 1296 ms, finished with status code: 200
3:10:29.503 PM
info
dialogflowFirebaseFulfillment
undefined
Please help

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
})

Write JSON to mysql database with node.js

I'm trying to write a JSON object (or string, unsure) to my mysql database using node.js. I first retrieved the JSON via an xml url using xml2js. I am able to log the json string result in my console via JSON.stringify, but I am unsure how to proceed from here.
Here is the url I took the xml from: https://water.weather.gov/ahps2/hydrograph_to_xml.php?gage=deld1&output=xml
I would like to write each instance from the JSON string to a row, with the columns as the name of the data. It would look something like this:
Here is my code in index.js, which I enact with node index.js on the console:
var parseString = require('xml2js').parseString;
var http = require('http');
var https = require('https');
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "password",
database: "mydb"
});
function xmlToJson(url, callback) {
var req = https.get(url, function(res) {
var xml = '';
res.on('data', function(chunk) {
xml += chunk;
});
res.on('error', function(e) {
callback(e, null);
});
res.on('timeout', function(e) {
callback(e, null);
});
res.on('end', function() {
parseString(xml, function(err, result) {
callback(null, result);
});
});
});
}
var url = "https://water.weather.gov/ahps2/hydrograph_to_xml.php?gage=deld1&output=xml"
xmlToJson(url, function(err, data) {
if (err) {
return console.err(err);
}
strungout = JSON.stringify(data, null, 1);
console.log(strungout);
//strungout contains my json string
})
con.connect(function(err) {
if (err) throw err;
//below is where I might make an insert statement to insert my values into a mysql table
var sql = someinsertstatement
con.query(sql, function (err, result) {
if (err) throw err;
console.log("records inserted");
res.end();
});
});
As mentioned, when I run the above code in my console, the console returns the JSON, though I am unsure how to assign this to a variable that I can then write into my mysql database.
Alternatively, if there is an easier way to write xml from a website directly to my mysql database, I would certainly appreciate any pointers. I feel like it should be easier than this, but I am new to pretty much all of it.
EDIT:
Adding the JSON. I removed the line breaks to consolidate it. Trying to assign the result '4.68' to a variable.
data = {"site": {"observed": [{"datum": [{"valid": [{"_": "2019-02-21T19:42:00-00:00","$": {"timezone": "UTC"}}],"primary": [{"_": "4.68","$": {"name": "Stage","units": "ft"}}]}]}]}};
Thank you.
This worked on my end. Found that the main data you seek is site.observed.datum
const parser = require('xml2json');
const request = require("request");
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "password",
database: "mydb"
});
var api_url = 'https://water.weather.gov/ahps2/hydrograph_to_xml.php?gage=deld1&output=xml';
function xmlToJson(url, callback){
return request({
method: 'GET',
url: api_url,
}, function (error, response, body) {
if (error) {
return callback({
errorResponse: error,
rowsToInsert: false
});
}else{
let jsonRes = JSON.parse(parser.toJson(body));
let datumResult = jsonRes.site.observed.datum;//I had to log Object.keys multple time to get the
const readyForDB = datumResult.map(x => {
let timeOfReading = x.valid.$t;
let stage = x.primary.$t;
let flow = x.secondary.$t;
return [
timeOfReading, stage, flow
]
});
return callback({
errorResponse: false,
rowsToInsert: readyForDB
});
}
})
}
return xmlToJson(api_url, ({errorResponse, rowsToInsert}) => {
if(errorResponse){
throw callback.errorResponse;
}
return con.connect(function(err) {
if (err) throw err;
//below is where I might make an insert statement to insert my values into a mysql table
var sql = "INSERT INTO forecast (timeOfReading, stage, flow) VALUES ?"
con.query(sql, [rowsToInsert], function (err, result) {
if (err) throw err;
console.log(result.affectedRows + " rows inserted");
});
});
});
Sounds like you have the JSON you want but are unsure how to access data within it. Correct me if I'm wrong.
Lets say you have this JSON object called "test":
{
a:1
b:{
x:2
}
}
You can access the value of 1 by calling test.a, and similarly access the value of 2 by calling test.b.x