Node.js await postgresql error - javascript

I use express and PostgreSql.
My Project Folder :
-Model
-Visitor.js
-app.js
I have connected postgresql with pg and i've tested with a query. It's running well. But, there is a query on Visitor.js file.
var visitor = function() {
this.add = function() {
var ret = false;
db.query({text: 'INSERT INTO visitor(visitorid, data, status) VALUES($1, $2, $3)', values:[id, JSON.stringify(data), "1"]}, function (err, response) {
if(!err) {
ret = true;
}
});
return ret;
}
}
This query always inserts a row in my table but returns false. This function should wait query to end. How can i do it ?

You should await on a promise
this.add = function() {
return new Promise((resolve, reject) => {
db.query({text: 'INSERT INTO visitor(visitorid, data, status) VALUES($1, $2, $3)', values:[id, JSON.stringify(data), "1"]}, function (err, response) {
if(!err)
resolve(response)
else
reject(err)
});
})
}
Then you can await on visitor.add function
EDIT
If you are using the pg library: as shown in Getting Started, you can directly await on the client.query function as it returns a Promise when you don't provide a callback
const { Client } = require('pg')
const client = new Client()
await client.connect()
const res = await client.query('SELECT $1::text as message', ['Hello world!'])
console.log(res.rows[0].message) // Hello world!
await client.end()

Related

accessing value of variable outside of async/await iife [duplicate]

This question already has answers here:
How to export asynchronously loaded data to be used in a different node module?
(1 answer)
How not to forget using await everywhere in Javascript?
(1 answer)
How can I export promise result?
(4 answers)
Closed last year.
I have the code below. I need to use the value of con in other functions but I have no possible solution for this. I tried using .thens and awaits like other stack articles but i kept getting the error that con is not valid.
I am truly stuck and unsure how to handle this.
service.js
async function connectToDatabase(secret) {
try {
const secretValue = await getSecretPromise(secret)
const { host, port, username, password, dbname } = JSON.parse(secretValue);
let con = mysql.createConnection({
host: host,
port: port,
user: username,
password: password,
database: dbname
});
con.connect(function(err) {
if (err) throw err;
console.log("Connected to database!");
});
return con;
} catch (error) {
console.error(error)
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let con = await connectToDatabase(secretName);
})();
function createUser({ data }) {
return new Promise((resolve, reject) => {
var sql = `query`;
con.query(sql, function (err, result) {
//does not work currently because ReferenceError: con is not defined
});
});
}
controller.js
async function getUsers(req, res, next) {
service.getUsers().then(function(val) {
res.json(val)
});
}
getsecretpromise
function getSecretPromise(secretName) {
return new Promise((resolve, reject) => {
client.getSecretValue({SecretId: secretName}, function(err, data)
{
//do stuff
}
}
service.getUsers
//this is in service.js
module.exports = {
createUser,
getUsers,
patchUser,
loginUser,
updateCallForward,
getEmail,
getCallForwardingNumber,
getDB
};
function getUsers() {
return new Promise((resolve, reject) => {
var sql = `sql`;
getDB().then(con => {
con.query(sql, function (err, result) {
if (err) throw err;
resolve(result);
});
});
});
}
You are defining the con variable after it's being returned, your IIFE starts working once it is read. What I am trying to say is you need to move that function up and if thats not an option then you need to define con before returning it. If you alredy defined it before this function then I dont know.
async function connectToDatabase(secret) {
// Start an IIFE to use `await` at the top level
(async function(){
let con = await connectToDatabase(secretName);
})();
try {
const secretValue = await getSecretPromise(secret)
const { host, port, username, password, dbname } = JSON.parse(secretValue);
let con = mysql.createConnection({
host: host,
port: port,
user: username,
password: password,
database: dbname
});
con.connect(function(err) {
if (err) throw err;
console.log("Connected to database!");
});
return con;
} catch (error) {
console.error(error)
}
}
function createUser({ data }) {
return new Promise((resolve, reject) => {
var sql = `query`;
con.query(sql, function (err, result) {
//does not work currently because ReferenceError: con is not defined
});
});
}
You would ideally do something like this. This would hide your instance(not really for this case) from rest of the code as you will access it through a getter function. Of course you need to import your secret as a constant. It doesn't make sense to pass it as a parameter unless you have multiple dbs
let con = null;
async function connectToDatabase(secret) {
try {
const secretValue = await getSecretPromise(secret)
const { host, port, username, password, dbname } = JSON.parse(secretValue);
con = mysql.createConnection({
host: host,
port: port,
user: username,
password: password,
database: dbname
});
con.connect(function (err) {
if (err) throw err;
console.log("Connected to database!");
});
return con;
} catch (error) {
console.error(error)
con = null;
}
}
export async function getDB() {
if (con == null) {
let secretObj = {};
return await connectToDatabase(secretObj);
}
else {
return Promise.resolve(con);
}
}
export function createUser({ data }) {
return new Promise((resolve, reject) => {
var sql = `query`;
getDB().then(con => {
con.query(sql, function (err, result) {
//does not work currently because ReferenceError: con is not defined
});
});
});
}
The code can be simplified (and repaired) by making promise-returning versions of the mysql callback style functions...
async function connect(connection) {
return new Promise((resolve, reject) => {
connection.connect(function (err) {
err ? reject(err) : resolve(connection)
});
})
}
async function query(connection, query) {
return new Promise((resolve, reject) => {
connection.query(query, function (err, results, fields) {
err ? reject(err) : resolve({results, fields});
});
});
}
Only these functions should create new promises explicitly. This cleans up the connectToDatabase function...
async function connectToDatabase(secret) {
try {
const secretValue = await getSecretPromise(secret)
const { host, port, username, password, dbname } = JSON.parse(secretValue);
let con = mysql.createConnection({
host: host,
port: port,
user: username,
password: password,
database: dbname
});
return await connect(con);
} catch (error) {
console.error(error)
}
}
And it cleans up createUser substantially...
async function createUser({ data }) {
try {
const connection = await connectToDatabase(secretName);
let {results, fields} = query(connection, 'query');
// and so on
} catch(error) {
console.log(error);
}
}
Not that getSecretPromise() might require the same treatment, but is not completely specified in the OP.
Other caveats: not tested (or even compiled). Relies on values in scope whose definitions are not shown in the OP (e.g. secretName).

Node.js constant is not waiting for response from async function

I am new to async/await and I'm trying to set a 'user'-constant as the return value of a MySQL query in Node.js. However, the constant does not wait for the return value of the function. How do I use async and await to set 'user' to the return value of the SQL query?
// Should return a user object
const getUserByUsername = async (username, db) => {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const user = await db.query(QUERY,
async (err, result) => {
if (!err) {
console.log("name in SQL function: " + result[0].username);
return await result[0];
} else {
console.log(err);
}
}
);
return user;
};
// Does stuff with the user object
const authenticateUser = async (username, password, done) => {
const user = await getUserByUsername(username, db);
console.log("name in main function: " + user.username);
// Trying to do stuff with the user object...
}
What I get in the terminal:
name in main function: undefined
UnhandledPromiseRejectionWarning: Error: data and hash arguments required
at Object.compare
at /app/node_modules/bcrypt/promises.js:29:12
at new Promise (<anonymous>)
at Object.module.exports.promise
etc.....
name in SQL function: john
When you use db.query with a callback, it does not return a promise
Try the following code instead
const getUserByUsername = async (username, db) => {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const result = await db.query(QUERY);
console.log("name in SQL function: " + result[0].username);
return result[0];
};
Please try the below code.
const getUserByUsername = async (username, db) => {
try {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const user = await db.query(QUERY, async (err, result) => {
if (err) {
throw err;
}
if (result && result.length) {
return result[0];
}
throw new Error(`User with username ${username} not found`);
});
console.log(`name in SQL function: ${user.username}`);
return user;
} catch (error) {
console.log(error);
throw error;
}
};
After jfriend00's comment, I decided to make my function a callback function. I found this answer explaining how to do it: https://stackoverflow.com/a/31875808/6817961. This worked!
My code now:
// Function returns a user object
const getUserByUsername = (username, callback) => {
const QUERY = `SELECT * FROM ${db_name} WHERE username = '${username}'`;
db.query(QUERY, (err, result) => {
if (!err) {
if (result.length > 0) {
return callback(null, result[0]);
} else {
err = "No user with that username";
return callback(err, null);
}
} else {
return callback(err, null);
}
});
};
// Then this function does stuff with the callback
const authenticateUser = (username, password, done) => {
getUserByUsername(username, async (err, result) => {
if (err) {
console.log(err);
return done(null, false, { message: err });
} else {
const user = result;
// result will now return the user object!

Getting Data from Azure SQL database in Azure Function

I'm having problems getting data from my AZURE SQL database. My code does get data, but not all of it. The intention is that the function needs to take all users in wicht the age is X (f.ex.:20)and return an array with those users. Right now the code just return the first user it finds on the database. I am using Azure-functions in which I use Insomnia to test the result.
Here is the function that gets the data from the DB:
function testfunc(age){
return new Promise ((resolve, reject) =>{
let result = [];
const sql = 'SELECT * FROM [datingschema].[user] where age = #age'
const request = new Request(sql, function(err){
if (err){
console.log("beforeerr");
console.log(err) //ingen err - så det godt nok!
console.log("aftererr");
reject(err);
}
})
request.addParameter('age', TYPES.Int, age)
request.on('row', (columns) => {
columns.forEach(column =>{
result.push(column.value)
})
resolve(result)
});
connection.execSql(request)
})
}
Here is a part of my code in Azure-function where I call for the function. There should be no errors in there as it works fine when I need to get only one user:
const db = require('../database/db');
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.')
try {
await db.startDb(); //start db connection
} catch (error) {
console.log("Error connecting to the database", error.message)
}
switch (req.method) {
case 'GET':
await get(context, req);
break;
case 'POST':
await post(context, req);
break
default:
context.res = {
body: "Please get or post"
};
break
}
}
async function get(context, req){
try{
let id = req.query.age
let user = await db.testfunc(id)
context.res = {
body: user
};
} catch(error){
context.res = {
status: 400,
body: `No User - ${error.message}`
}
}
}
The error happens, because you resolve your promise after you have read the first row. Consider the following:
function testfunc(age){
return new Promise ((resolve, reject) =>{
let result = [];
const sql = 'SELECT * FROM [datingschema].[user] where age = #age'
const request = new Request(sql, function(err){
if (err){
console.log("beforeerr");
console.log(err) //ingen err - så det godt nok!
console.log("aftererr");
reject(err);
}
})
request.addParameter('age', TYPES.Int, age)
// This is executed multiple times, once for each row
request.on('row', (columns) => {
let row = []
// Accumulate the columns to a row
columns.forEach(column =>{
row.push(column.value)
})
// Don't resolve here. Instead append to result..
// resolve(result)
result.push(row)
});
// This is executed once, when the query has completed
request.on('done', () => {
// .. and resolve here
resolve(result)
})
connection.execSql(request)
})
}

Make query for every object in json using for or forEach

My problem is, I want to make INSERT query for every object from JSON using some loop, but I almost always got an error "Cannot set headers after they are sent to the client".Can someone help?Tnx
const connection = require('./config');
module.exports.excel = function (req, res) {
var _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
var jsonData = req.body;
var values = [];
function database() {
return new Promise((resolve, reject) => {
jsonData.forEach((value) => {
values.push([value.id, value.first_name, value.last_name]);
connection.query(_query, [values], (error, results) => {
if (error) {
reject(
res.json({
status: false,
message: error.message
}))
} else {
resolve(
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
}))
}
});
});
})
}
async function write() {
await database();
}
write();
}
After I got JSON from my Angular 6 front I put req.body into jsonData and try with forEach to put every object("value" in this case) into query and write that into Excel file.
You will have to wrap each query in a Promise and wait for all to complete before sending the response using Promise.all
Not that database() is going to throw when one of the queries fail and you won't have any access to the resolved promises.
const connection = require('./config');
module.exports.excel = function(req, res) {
const _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
const jsonData = req.body;
function database() {
return Promise.all(
jsonData.map(
value =>
new Promise((resolve, reject) => {
const values = [value.id, value.first_name, value.last_name]
connection.query(_query, [values], (error, results) => {
if (error) {
reject(error.message);
return;
}
resolve(results);
});
})
)
);
}
async function write() {
try {
const results = await database();
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
});
} catch (e) {
res.json({
status: false,
message: e.message
});
}
}
write();
};

how to wait till DB connection is made and queries are executed for each database in an array

A file contains json data with details of database.
For each database connection, a series of queries need to be executed.
Currently, the map function is waiting for the database connection.
Below is the start function
function start() {
console.log('function initiated');
try {
let jsonData = fs.readFileSync('../request.json');
let jsonString = JSON.parse(jsonData);
//jsonString['request'].forEach(async function(json) {
jsonString['request'].map(async json => {
dbdetails = json.dbdetails;
//dbdetails.forEach(async function(db){
await dbbdetails.map(async db => {
console.log('pdbdetails: ' + db);
connString = json.connString;
//makes the DB connection
await connectDB(db.userId, db.Password, connString)
.then(async conn => {
await execution(conn, pdbDetails, vmUser, vmPassword, ip);
})
.catch(err => {
console.log(err);
});
console.log('after each execution');
//}
});
});
} catch (err) {
console.log(err.message);
return;
}
}
Below function is to make a database connection and return the connection
function connectDB(oUser, oPassword, connString) {
console.log('inside connectDB');
return new Promise((resolve, reject) => {
oracledb.getConnection(
{
user: oUser,
password: oPassword,
connectString: connString
},
function(err, connection) {
if (err) {
console.error(err.message);
reject(err);
//throw err;
}
console.log('returning connection');
//console.log(connection);
resolve(connection);
//return connection;
}
);
});
}
below is the function which executes servies of queries on database
function execution() {
/// series of sql query execution
}
Not sure what you’re trying to do exactly, but sounds like the problem is that .map doesn’t wait for your async functions. If you have to do them one at a time, use a for loop:
for ( var item of array ) {
await item.something();
}
To do them all at once:
var results = await Promise.all( array.map( item => item.something() )

Categories

Resources