using async with mysql - javascript

So i got the following code. I want to check if Name already exists in my database. If not I want to insert it. And it basically works with my code. My only problem is, that the last bit of code where it checks whether "success" is true gets executed before it even checked the database. (when using a Name that doesn't exist in the database my console is like this:
Name is already taken <--- this piece of code should be executed last. Then that would not appear
No Duplicate, insert it
starting insert function
I've heard of async and await but I just could not figure it out. I could really need some help.
Here is my Code:
app.post("/api/createProject", (req, res) => {
var success = false;
// console.log(req.body);
const Name = req.body.Name;
const Status = req.body.Status;
const Deadline = req.body.Deadline;
const EstimatedHours = req.body.EstimatedHours;
const BudgetHours = req.body.BudgetHours;
const BudgetCost = req.body.BudgetCost;
const Description = req.body.Description;
// check if Projct Name is already taken
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
return err;
} else {
if ((results.length < 1)) {
console.log("No Duplicate, insert it")
insertDatabase();
}
}
});
// will only get executed when no Errors got detected above (Name is not already taken)
function insertDatabase() {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours, BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
Name,
Status,
Deadline,
EstimatedHours,
BudgetHours,
BudgetCost,
Description,
],
(err, result) => {
if (!err) {
success = true;
}
}
);
}
if (success == true){
return 0;
} else {
console.log("Name is already taken");
return "Name is already taken";
}
});

Your issue is to do with the asynchronous operation of NodeJS. What is happening in your code is all of your functions are getting "fired" because the code will execute sequentially, but it does not wait for the functions to return.
This is why your if / else statement is getting executed as the "else" - the query hasn't returned to set the "success" to true and therefore it hits the else statement because of its default value.
So we need to refactor your code a bit to make it suitable for async-await. The async-await will wait for a function to return before carrying on to the next statement. With the async-await command, the function it is calling must return a promise.
First we abstract your query into a function with a promise. A promise will run your query and only return when your query is complete:
async function Query (request) {
return new Promise ((resolve, reject) => {
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${request.Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results)
}
});
})
}
The above function takes request, which would be your req.body as a parameter, carries out the query and returns the results.
We also need to refactor your insertDatabase function into async-await to see if our insert was successful or not (as this seems to be what you want to know):
async function insertDatabase(request) {
return new Promise((resolve, reject) => {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours,
BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
request.Name,
request.Status,
request.Deadline,
request.EstimatedHours,
request.BudgetHours,
request.BudgetCost,
request.Description,
],
(err) => {
if (err) {
reject(false)
}
else {
resolve(true)
}
});
});
}
We then check the results for length less than 1:
var result = await Query(req.body);
if (result <1) {
console.log("No Duplicate, insert it")
success = await insertDatabase(req.body);
}
Putting it all together our code will look something like:
app.post("/api/createProject", async (req, res) => {
var success = false;
var result = await Query(req.body);
if (result <1) {
console.log("No Duplicate, insert it")
success = await insertDatabase(req.body);
if (success == true){
return 0;
} else {
console.log("Name is already taken");
return "Name is already taken";
}
}
}
async function Query (request) {
return new Promise ((resolve, reject) => {
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${request.Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results)
}
});
})
}
async function insertDatabase(request) {
return new Promise((resolve, reject) => {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours,
BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
request.Name,
request.Status,
request.Deadline,
request.EstimatedHours,
request.BudgetHours,
request.BudgetCost,
request.Description,
],
(err) => {
if (err) {
reject(false)
}
else {
resolve(true)
}
});
});
}
Notice our post callback has "async" in front of it. This is a required keyword in any function that contains the await command.
Additional:
I've left your code mostly how it was with a bit of refactoring, some extra code best practice notes for you:
Multiple return paths is a very bad idea as this leads to testing issues as well as later debugging nightmares, so I would suggest you refactor the return from the final if statement - maybe assign the variables in the if statement but return the variable at the end.
You shouldn't return different types from the same function. So either return 1 or 0 (true or false) on your result, or return two different sets of text. Do not return 0 for one case and text for another, this is very bad practice.
I've also short-cutted by passing your req.body into the functions which probably passes more data than needed. If you don't want to pass this, as a minimum you should encapsulate your assignments that go together into a JSON object:
var request = {
"Name" : req.body.Name,
"Status" : req.body.Status,
"Deadline" : req.body.Deadline,
"EstimatedHours" : req.body.EstimatedHours,
"BudgetHours" : req.body.BudgetHours,
"BudgetCost" : req.body.BudgetCost,
"Description" : req.body.Description
}
and then pass this variable request into the functions instead of req.body.

i'm also relatively new to this, but have an idea.
Try this:
make your function async
async function insertDatabase()
then await your query
await db.query()

Related

Export an array containing mysql results to another javascript file

I making use of node.js + mysql in my app. I would like to export an array containing results/rows of an SQL select statement to another javascript file and display the values stored in the array.
stats.js
var phonenum='0718900000'
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql,[phonenum],function(err,results){
if (err){
throw err;
};
/*if (results==0)
{
let dealersMessage="❌ User not found"
}*/
console.log(results)
var datastatagentStore=[];
results.forEach(item =>{
module.exports=datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
})
app.js
var{datastatagentStore}=require('../functions/stats.js')
console.log('User details '+datastatagentStore);
Based on a comment above:
My goal is to execute the query and display the results to the user every time the user selects the 'view my details' button.
Then you don't want to export the results, you want to export the function which performs the query. That function would return [a Promise which resolves to] the results.
If conn.query returns a Promise then it might look something like this:
var getUserDetails = function (phonenum) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
return conn.query(sql, [phonenum], function(err, results) {
if (err) {
throw err;
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
return datastatagentStore;
});
};
module.exports = getUserDetails;
Though I don't know if conn.query does return a Promise. If it doesn't, you can manually create one:
var getUserDetails = function (phonenum) {
return new Promise(function (resolve, reject) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql, [phonenum], function(err, results) {
if (err) {
reject(err);
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
resolve(datastatagentStore);
});
});
};
module.exports = getUserDetails;
Note the use of resolve and reject here to manually complete the Promise. But however you structure it, the overall point is that your module export wouldn't be the results of having executed the query, it would be the function to execute the query. And since executing the query is an asynchronous operation, that function should return a Promise.
Then you can use it like any other asynchronous operation:
var datastatagentStore = require('../functions/stats.js');
datastatagentStore('0718900000').then(function (details) {
console.log('User details ', details);
});

Function returning empty array, how to solve it? Javascript, mongoose

I have a javascript, mongoose function which should return an array. So basically what I have is a user schema, and a shopping list schema. User schema has a parameter which is authorizedLists, an array of shopping lists ids. What I want is, that by passing the user id as a parameter, I can show those lists the user is authorized in. This function right now returns an empty array, and what I expected was an array with the shopping list objects.
I know it is not working due to Javascript asynchronous way of reading code, but I´m really stucked here.
Also I´m relatively new to nodejs and mongoose so I may have made other mistakes but haven´t realized yet.
Code:
function getUserAuthLists(req,res){
const { userId } = req.params;
var list = [];
let i = 0;
User.findById( userId,async function(err, user){
if(err) return res.status(404).send(err);
console.log(user);
const array = user.authorizedLists;
console.log(array);
array.forEach( function(element){
console.log(element);
ShoppingList.findById(element , (error, result)=>{
if(error) {
console.log(error);
return res.status(404).send(error);
}
list.push(result);
console.log(list);
});
i++;
});
if(i === array.length) {
console.log(list);
return res.status(202).send(list);
}
});
}
Thanks in advance.
Refactor your function to use promises, collect all authorizedLists and pass it in one-go using the $in operator instead of repeatedly doing findById
async function getUserAuthLists(req, res){
try {
const { userId } = req.params;
let i = 0;
const user = await User.findById(userId).exec();
const list = await ShoppingList.find({
_id: {
$in: user.authorizedLists
}
}).exec();
return res.status(202).send(list);
} catch (err) {
return res.status(404).send(err);
}
}
Since ShoppingList.findById is an i/o call, control does not wait by default to finish this call and directly jump to list.push(result); with list empty values.
You need to use promise to make it wait until it gets results from i/o call and then you should send back response. That way list will be updated for each array item call.
Something like this-
async function getUserAuthLists(req, res) {
User.findById(userId, async function (err, user) {
if (err) return res.status(404).send(err);
console.log(user);
const array = user.authorizedLists;
console.log(array);
await ShoppingListFromDB(array)
.then((list) => {
console.log("list--", list);
return res.status(202).send(list);
})
.catch((error) => {
console.log(error);
return res.status(404).send(err);
})
});
}
function ShoppingListFromDB(array) {
return new Promise((resolve, reject) => {
let i = 0
var list = [];
for (const element of array) {
console.log("element ", element);
ShoppingList.findById(element, (error, result) => {
if (err) {
console.log(err)
return reject(error);
}
i++
list.push(result)
if (i == array.length) {
return resolve(list)
}
})
}
})
}
I have created a sample that simulated your requirement.
https://github.com/ajaysikdar/test_server
server
client

Sqlite .all() function returns an undefined promise. How can I use the result further?

I am just starting to work with JS and SQLite. And could not find any help for my specific question.
I want to keep a return to use it as a foreign key in a second table.
This is my function:
async function getIdbyName(table, row, name) {
let nameNeu = '"' + name + '"';
let sql =
"SELECT id as print FROM " +
table +
" WHERE " +
row +
" = " +
nameNeu +
" LIMIT 1;";
// await db.get(sql, (err, row) => {
// console.log(row.print);
// return row;
// });
return await db.get(sql);
}
getIdbyName("...", "...", "...")
.then(function (value) {
console.log("Success!", value); // <-- prints: Success! undefined
})
.catch(function (err) {
console.log("Caught an error!", err);
});
console.log(getIdbyName("r_Tag", "r_Tag", "test")); //<-- shows me a Promise
What do I have to do so that promise does not stay undefined outside of the function?
Rest of the code:
var sqlite3 = require("sqlite3").verbose();
let db = new sqlite3.Database("./assets/db/test.db", (err) => {
if (err) {
return console.error(err.message);
}
console.log("Connected to the SQlite database.");
});
My other function just creat some strings and I run a few times db.run(...) to add some tables.
To put it more plainly, your getIdByName function never returns anything. You need to return the value you get back from await db.get(...). Once you do that, when you call getIdByName, you should get your response from the database.
You should also know that your code is susceptible to SQL injection, which is a major security vulnerability. Instead of concatenating a string, you should use a prepared statement.
async function getIdbyName(table, row, name) {
return await db.get(sql);
}
Update: Promise Wrapper for SQLlite - Aug 1, 2020
Based on this blog post, it seems it's not possible to do native async/await using sqlite3. However, you can write a wrapper function around db.all to return a promise, which will allow you to use async/await. Note the use of ? in the SQL statement, which will be replaced by the values of the array in the second argument following the same order. For more help with parameterized queries, read the params bullet point in the documentation here.
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("./assets/db/test.db", (err) => {
if (err) {
return console.error(err.message);
}
console.log("Connected to the SQlite database.");
});
db.query = function (sql, params = []) {
const that = this;
return new Promise(function (resolve, reject) {
that.all(sql, params, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
async function getIdByName(table, name) {
// assemble sql statement
const sql = `
SELECT id
FROM ?
WHERE name = ?;
`;
return await db.query(sql, [table, name]);
}
// need async to call
(async () => {
const result = await getIdByName('books', 'my_name');
console.log(result);
})();

Returning value from async function node.js

I know it is the most frquently asked question in javascript regarding asynchronous behaviour of functions. Still I am not able to figure out a working solution for my use case.
What I am trying to do is lookup the redis cache using a key. I am using the exists function to check the key is present or not. If present then i'll return from cache if not then i'll set the key and then make a db call using this key as a db param. It seems very simple but no matter what I do i'm unable to return the value from the cache or the DB. If I make these calls outside the exist function then it works as the resolver function(graphql resolver) is an async function. This resolver functions expects a return value.
So here is the code by which i'm unable to retun the value in any scenario:-
empId: async(obj, params, ctx, resolverInfo) => {
await client.exists(obj.empId, async function(err, reply) {
if (reply == 1) {
return await getAsync(obj.empId).then(res => {
console.log(res);
return res;
})
} else {
return await db.one('SELECT * FROM iuidtest WHERE empid = $1', [obj.empId])
.then(iuidtest => {
console.log(iuidtest.empid);
return iuidtest.empid;
})
}
});
const doSomethingWith = empid => {
console.log("empid = ", empid);
}
I am getting the correct values in console but am unable to return. However if I directly make these calls in my resolver function i.e. outside of the redis exists function I am able to return the value.
empId: async(obj, params, ctx, resolverInfo) => {
return await getAsync(obj.empId).then(res => {
console.log(res);
return res;
This way I am able to return the value from the resolver function. It would be really of great help if anybody can provide the working code for this instead of other links regarding how to return from async function using callbacks and promises. Here is another post reg the same. :-
Redis async library does not have function that are in redis library for node.js
Thanks in advance!
If client.exists returns promise, the same code can be written as below:
empId: async (obj, params, ctx, resolverInfo) => {
const exists = await client.exists(obj.empId);
if (exists === 1) {
return getAsync(obj.empId);
}
return await db.one('SELECT * FROM iuidtest WHERE empid = $1', [obj.empId])
.then(iuidtest => {
return iuidtest.empid;
});
}
If client.exists only accepts callback, then the code can be written as:
empId: async (obj, params, ctx, resolverInfo) => {
async function empIdExists(empId) {
return new Promise(function resolver(resolve, reject) {
client.exists(obj.empId, function(err, reply) {
if (err) {
reject(err);
return;
}
if (reply == 1) {
resolve(1);
return;
} else {
resolve(0);
return;
}
})
});
}
const exists = await empIdExists(obj.empId);
if (exists === 1) {
return getAsync(obj.empId);
}
return await db.one('SELECT * FROM iuidtest WHERE empid = $1', [obj.empId])
.then(iuidtest => {
return iuidtest.empid;
});
}
In the second version, notice that I have wrapped the client.exists call into an async function & called using await keyword.

Variable not changing in function Javascript

I have written some code where I have a variable that changes if I enter an if statement inside the function of an sql query. However the function does not change globally only locally. The code is the following:
app.post("/energy/api/Admin/users", function(req,res)
var correct=0
sql.query(`SELECT apikey,privileges FROM users WHERE apikey=?`,[req.user.apikey],(err,res1) => {
if (err) {
console.log("error: ", err);
result(err, null);
return;
}
else if (res1.length && res1[0].privileges=="superuser"){
console.log("Apikey correct");
correct=1;
}
else correct=0;
});
console.log(correct);
if (correct==1){
....
}
Note: The if (correct==1) never happens even if correct is set to 1 inside the query function. I think this is not supposed to happen with variables in javascript. Why does this happen?
Change it it promise style as you are trying to access the value before even it is set. the code will look like this
app.post("/energy/api/Admin/users", async function (req, res) {
const correct = await getResults(req.user.apikey);
console.log(correct);
if (correct == 1) {
}
});
function getResults(key) {
return new Promise((resolve, reject) => {
sql.query("SELECT apikey,privileges FROM users WHERE apikey=?", [key], (err, res1) => {
if (err) {
console.log("error: ", err);
reject(err);
} else if (res1.length && res1[0].privileges == "superuser") {
console.log("Apikey correct");
resolve(1);
} else {
resolve(1);
}
});
});
}
The query function is indeed asynchronus. The easy way is to work with async await which handles the promise responses. You could try this:
app.post("/energy/api/Admin/users", async function(req,res)
var correct=0
let response = await sql.query(`SELECT apikey,privileges FROM users WHERE apikey=?`,[req.user.apikey]);
// then here you can check for the response if its an error and so on
if (response) {
console.log("response: ", response);
//... your code now you can use the response without thiunking of asynchronity

Categories

Resources