NodeJS SQL Server data pull isn't waiting for promise completion - node.js

I am rewriting an ASPX application using Node. Part of this application is pulling data from a table in SQL Server. I am doing this using async functions to try and force the data to be pulled prior to continuing on. It is essential that I have the data before the next step. However, when it returns from the await the response is null.
const getUserGroups = async (user) => {
let qry = `Select role from [xxxxx] where LOWER(yyyyy)='${user.toLowerCase()}'`;
let groups = await dataQuery.Select(qry, dbList.MCAIntranet)
console.log("Groups: ", groups); //This is undefined
return groups;
// })
}
Here is the Select function:
const Select = async (query, database) => {
// console.log(config[database]
sql.connect(config[database], (err) => {
if (err) console.log(err);
let request = new sql.Request();
request.query(query, (err, recordset) => {
console.log(recordset)//displays correct data
if (err) {
console.log(err);
return null
}
return recordset;
})
});
}
I am using the mssql module, but since I am fairly new to Node I am uncertain where my mistake lies.
Thanks in advance for looking at this.

In you current code, you don't return anything from your Select function. Espectially you are not wrapping your sql code in promise.
You say, you are using the mssql package. This already supports async/await, so you should use the apropriate async calls instead of callbacks.
const Select = async (query, database) => {
try {
await sql.connect(config[database]);
let request = new sql.Request();
let recordset = await request.query(query);
return recordset;
} catch (e) {
console.log(e);
return null;
}
}
Furthermore you should read about parameterized queries, because currently your code is quite unsafe, ie it is vulnerable to SQL injections and invalid syntax errors.

You can either wrap your implementation with a Promise object, or return Promise.resolve(data);, and return Promise.reject(err):
const Select = async (query, database) => {
return new Promise((resolve, reject) => {
// console.log(config[database]
sql.connect(config[database], (err) => {
if (err) reject(err);
let request = new sql.Request();
request.query(query, (err, recordset) => {
console.log(recordset)//displays correct data
if (err) {
console.log(err);
reject(err);
}
resolve(recordset);
})
});
});
}

Related

NodeJS Promises do not return AWS Athena Error

I've written a web interface to allow users to query AWS Athena. It is using NodeJS Promises with Async and Await, but the error handling is not good. When a user enters an invalid SQL query (table not found), the response is always "{}". I've tried Athena-express and Athena-client. I then tried the Athena-client "toStream()" syntax.
var stream = client.execute('SELECT * from sometable limit 1').toStream()
stream.on('data', function(record) { console.log('record='+JSON.stringify(record)) })
stream.on('query_end', function(queryExecution) { console.log(queryExecution) })
stream.on('end', function() { console.log('end') })
stream.on('error', function(e) { console.error(e) })
And it returned an error. I thought great, let me encapsulate it in a promise. I've used:
new Promise((resolve, reject) => ...
require('stream/promises');
require('stream-promise');
Essentially, the moment I put it in a promise, the error I get back is always "{}". Does anyone know how you can get the actual Athena error returned using promises without it crashing the app?
let executeQuery = new Promise((resolve, reject) =>
{
let response = client.execute('SELECT * from sometable limit 1').toStream();
response.on('error', (err) => { reject(err); })
response.on('finish', function() { resolve(response); })
})
async function run()
{
try
{
let response = await executeQuery;
console.log('response='+JSON.stringify(response));
}
catch(e) { console.log('MY error='+JSON.stringify(e)) }
}
run();
It returns "My error={}", and I need it to return "Table sometable does not exist"
Found my issue. I can't JSON.stringify(e) I have to JSON.stringify(e.message)
const output = JSON.stringify(e.message);
That was annoying!

Database call returns undefined promise

I am trying to write an api that queries a mssql database. however my response from the api is always Promise { undefined }
app.get('/api/:year/:month', (req, res) => {
var data = getData(req.params.month, req.params.year);
console.log(data);
res.send(data);
});
async function getData(m, y) {
var query = "SELECT ...";
sql.connect(Config, (err) => {
if (err) {
console.log("Error while connecting to database :- " + err);
} else {
var request = new sql.Request();
request.query(query, function (err, rs) {
if (err) {
console.log("Error while querying database :- " + err);
} else {
return rs.recordset;
}
sql.close();
})
}
})
I'm assuming I need to await the result somehow as if I log the response inside the data function it is populated, however I can't seem to get anything to work.
You are creating it a callback style function but not really taking the value. You need to convert it to promise based like this and use async/await
app.get("/api/:year/:month", async (req, res) => {
const data = await getData(req.params.month, req.params.year);
console.log(data);
res.send(data);
});
async function getData(m, y) {
const query = "SELECT ...";
await sql.connect(Config);
const request = new sql.Request();
const rs = await request.query(query);
return rs.recordset;
}

Returning value within function

I have the following function:
const findUserByEmail = (email) => {
var user
MongoClient.connect(url, (err, db) => {
const dbo = db.db('users')
dbo.collection('users').findOne({email: email}, (err, result) => {
if (err) throw err
else return result
})
db.close()
}).then((user) => {
return user
})
}
I am trying to return the value of user so that findUserByEmail(email)=user but can't figure out how. I have tried using async/await and promises to return this value to get around Node's asynchronous nature however in each I could not get the value to return to the main function. In this case, return user returns the user to the then() function, which is not correct.
Any help would be much appreciated.
Your very close but there is one thing your missing with your function findUserByEmail it needs to return a Promise. With a Promise you can call resolve in the future with the result from findOne. This will also change how you consume the findUserByEmail function.
Example
const findUserByEmail = (email) => {
return new Promise((resolve, reject) => {
MongoClient.connect(url, (mongoError, db) => {
if (mongoError) {
return reject(mongoError)
}
const dbo = db.db('users')
dbo.collection('users').findOne({ email: email }, (findError, user) => {
if (findError) {
return reject(findError)
}
db.close();
return resolve(user)
})
})
})
}
To consume this function you can use Promise.then(user => ) or the preferred approach of using async/await.
Consuming using .then()
findUserByEmail("email.com").then(user => {
// do something
}).catch(err => {
// do something
})
Consuming using async/await
async function test() {
try {
const user = await findUserByEmail("email.com");
// do something
} catch (error) {
// do something
}
}

Returning the result of a node-postgres query

I am attempting to return the result of a node-postgres query and store it in a variable. I can manage a console.log just fine, but cannot find a way to return the result so that it is accessible outside of the query method. I'm at a loss, and know I must be missing something obvious (or multiple obvious things), because if it isn't possible to do this I don't understand the point of node-postgres since all I will ever be able to do is log my results to the console.
I have tried the code below, along with a version using promises, and both receive the same result, 'undefined.' A console.log within the else works fine, but a return does not make the result accessible to the rest of the function. In the code below, my return comes back as 'undefined' as would a console.log in its place.
var selectFrom = function(data, table, condition) {
var queryResult;
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err) { console.log(err); }
else { queryResult = result.rows[0][data]; }
})
pool.end();
return queryResult;
}
var result = selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`);
console.log(result);
This is what you are looking for:
async function selectFrom(data, table, condition) {
try {
const res = await pool.query(
`SELECT ${data} FROM ${table} ${condition}`
);
return res.rows[0][data];
} catch (err) {
return err.stack;
}
}
Outside of the previous function:
async function whateverFuncName () {
var result = await selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`);
console.log(result);
}
EDIT: I didn't run this code snippet but the main concept is pretty straightforward.
The "query" method is an async call, you should use async/await or Promise.
With async/await:
await client.connect()
const res = await client.query("SELECT amount FROM total_nonfarm_monthly_sa WHERE month='2019-08-31'");
console.log(res.rows[0]);
await client.end();
Edit:
I can see that there's an option of callback, but I would use the async/await
You need to use callbacks:
var selectFrom = function(data, table, condition, callback) {
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err)
return callback(err);
callback(null, result.rows[0][data]);
})
pool.end();
}
selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`, function(err, result){
console.log(err, result);
});
or promises:
var selectFrom = function(data, table, condition) {
return new Promise(function(resolve, reject){
pool.query(`SELECT ${data} FROM ${table} ${condition}`, function(err, result) {
if(err)
return reject(err);
resolve(result.rows[0][data]);
})
pool.end();
});
}
selectFrom('amount','total_nonfarm_monthly_sa', `WHERE month='2019-08-31'`)
.then(function(result){
console.log(result);
}).catch(function(err){
console.log(err);
});

How do I return Cloudant insert status?

I have the following code:
updateDocument = (data) => {
Cloudant(CONN_STRING, (err, cloudant) => {
if (err) {
throw new Error('Failed to initialize Cloudant: ' + err.message);
}
let db = cloudant.db.use('my-db')
db.insert(data, (err, result) => {
if (err) {
throw new Error('Failed to initialize Cloudant: ' + err.message);
}
else {
console.log(result);
}
})
});
}
I would like for updateDocument() to return the result db.insert provides. However, if I try to return a variable it is undefined (I believe because of the async calls). I've tried async and await, but I may not have set them up properly because they also didn't work.
Thanks for any help!
This is a "JavaScript is asynchronous" problem. You could simplify your updateDocument function like so:
updateDocument = (data) => {
var cloudant = Cloudant({ url: CONN_STRING, plugin: 'promises');
let db = cloudant.db.use('my-db')
return db.insert(data);
}
This is using the Cloudant library with the "promises" plugin. JavaScript Promises help you manage asynchronous calls without passing callback functions around.
You would call this code like so:
updateDocument({a: 1, :b})
.then((data) => {
console.log('success!', data);
}.catch((e) => {
console.log('oops!', e);
});
Success arrives at 'then', failure arrives in the 'catch' clause.

Resources