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)
})
}
Related
My backend is consist of Api and DB. When I want to get response from DB I have had delayed output by 1 query.
API (I think api is ok. Start read DB first)
app.post('/api/query', (req, res) => {
console.log(`\n Query input : ${JSON.stringify(req.body)}`);
let queryInput = (Object.values(req.body).join(' '));
if(!dbApi.checkArray(queryInput)){ //If array is not made from clear strings
res.json(dbApi.queryFromUser(queryInput));
}
else{
res.json(dbApi.queryOutput);
}
});
app.listen(dbConfig.server.port, () =>
console.log(`Server running on port ${dbConfig.server.port}`));
DB
queryOutput = [];
const receivingQuery =(queryInput) => {
db.query(queryInput, (err, result) =>{
if(err) throw err+' : '+queryInput;
queryOutput = result;
console.log("\nQuery output "+ JSON.stringify(queryOutput)); //Output (result) is ok
});
return queryOutput //Here is Output from previous query (sends to API)
}
module.exports = {
queryOutput: queryOutput,
queryFromUser: receivingQuery,
}
I tryied callback method and I rewrite it couple of times. But I dont have enough skill to solve it.
If You want to return result of query so simply do following things:
add query method to db module:
function query(sql, args = []) {
return new Promise(function(resolve, reject) {
db.query(sql, args, (err, result) => {
if (err) return reject(err);
resolve(result);
});
});
}
// extra feature, getting user by id
async function getUserById(id) {
const result = await query('SELECT * FROM users WHER id = ? LIMIT 1', [id]);
if (Array.isArray(result) && result[0]) return result[0];
return null;
}
module.exports = {
query,
getUserById, // export user by id
queryOutput,
queryFromUser: receivingQuery,
}
use it (with async and await):
app.post('/api/query', async (req, res) => {
try {
console.log('Query input:', req.body);
const queryInput = Object.values(req.body).join(' ');
const result = await dbApi.query(queryInput);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.get('/api/users/:id', async (req, res) => {
try {
const user = await dbApi.getUserById(req.params.id);
if (!user) return res.status(404).json({message: 'User not found'});
res.status(200).json(user);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.listen(dbConfig.server.port, () =>
console.log('Server running on port', dbConfig.server.port));
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();
};
I'm calling a function to see if an email adress is in the db. It's either an empty or filled array. Whenever i return this value it is undefined afterwards. How can I solve this? Thanks in advance!
I have tried using next() and promises.
The function that calls the db query function.
const emailExists = async function(req,res,next){
let emailInDb = await usermodel.getOneByEmail(req, next);
console.log(emailInDb);
if(emailInDb.length !==0){
res.send('email already in use!');
}
else{
return next();
}
};
The db query.
const getOneByEmail = function (req, next){
let db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to books db.');
});
db.all(`SELECT * FROM users WHERE email = ?`, [req.body.email],
(err, rows) => {
console.log(rows);
return rows;
});
};
console.log(emailInDb) gives undefined.
While console.log(rows) gives an array with results.
Putting await in front of a non async function does not magically make the function wait for the db-calls to resolve. You need to wrap the db-calls in a promise, e.g:
const getOneByEmail = (req, next) => {
return new Promise((resolve, reject) => {
let db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error(err.message);
reject(err);
}
console.log('Connected to books db.');
});
db.all(`SELECT * FROM users WHERE email = ?`, [req.body.email],
(err, rows) => {
// TODO: check for error here and reject
console.log(rows);
resolve(rows);
});
});
};
Note that you shouldn't connect to the database each time you call getOneByEmail, instead reuse the connection...
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() )
I'm trying to get async / await to trigger events in order, but it seems I'm missing something as my console.log markers are triggering in reverse to the order I was hoping for.
I 'm wondering if is to do with my use of nested functions in users.js but having tried multiple variations of async / await, it consistently doesn't work as expected.
// index.js
var users = require("./users.js");
app.post("/getToken", async function(req, res) {
if (req.body.email && req.body.password) {
const email = req.body.email;
const password = req.body.password;
const user = await users(email, password)
// running this should output console.log("No 1")
// from users.js first, but doesn't ?
console.log('No 2')
if (user) {
var payload = {
id: user.id
};
var token = jwt.encode(payload, cfg.jwtSecret);
res.json({
token: token
});
} else {
res.sendStatus(401);
}
} else {
res.sendStatus(401);
}
});
// users.js
module.exports = function(emailAddress, password) {
db.connect();
var query = `
SELECT
id,
email,
password,
salt
FROM
users
WHERE
email = ?`;
var query_params = [emailAddress];
db.query(
query,
query_params,
function(error, result, fields) {
console.log('No 1')
if (error) throw error;
if ( result.length == 1 ) {
if ( checkPass(password, result[0].password, result[0].salt ) ) {
return { id: result[0].id }
} else {
console.log("login False | Password");
return false;
}
} else {
console.log("login False | username");
return false;
}
}
)
}
Your users.js function doesn't return anything. The callbacks you're passing query do, but the overall function doesn't. Since it never returns anything explicitly, the result of calling it is undefined. If you await undefined, it's like await Promise.resolve(undefined) and so your resolution handler is called quite quickly.
You want that function to return a promise that doesn't get resolved until the work is done. Since what it uses is an old-style Node callbck API, it's reasonable to use new Promise to create that promise (alternately, get or create a promise-enabled API to that DB).
I also suspect you're calling connect incorrectly, since normally that would be an asynchronous action, but you're treating it as though it were synchronous.
See comments:
users.js
module.exports = function(emailAddress, password) {
return new Promise((resolve, reject) => {
// Use the callback to know when the connection is established
db.connect(error => {
if (error) {
// Connection failed
reject(error);
return;
}
var query = `
SELECT
id,
email,
password,
salt
FROM
users
WHERE
email = ?`;
var query_params = [emailAddress];
db.query(
query,
query_params,
function(error, result, fields) {
// Throwing an error here does nothing useful. Instead,
// reject the promise.
if (error) {
reject(error);
return;
}
// Resolve our promise based on what we got
if ( result.length == 1 ) {
if ( checkPass(password, result[0].password, result[0].salt ) ) {
resolve({ id: result[0].id });
} else {
console.log("login False | Password");
resolve(false);
}
} else {
console.log("login False | username");
resolve(false);
}
}
);
});
});
}
Then using it:
app.post("/getToken", async function(req, res) {
// You must handle errors, since `post` won't do anything with the return
// value of this function
try {
if (req.body.email && req.body.password) {
const email = req.body.email;
const password = req.body.password;
// Now this waits here, since `users` returns a promise that
// isn't resolved until the query completes
const user = await users(email, password)
console.log('No 2')
if (user) {
var payload = {
id: user.id
};
var token = jwt.encode(payload, cfg.jwtSecret);
res.json({
token: token
});
} else {
res.sendStatus(401);
}
} else {
res.sendStatus(401);
}
} catch (e) {
res.sendStatus(401);
}
});
The problem is that db.query function is asynchronous - you are providing callback function that is executed when database call is finished. You probably need to wrap this whole function in Promise:
module.exports = function(emailAddress, password) {
return new Promise(function(resolve, reject) {
db.connect();
var query = `
SELECT
id,
email,
password,
salt
FROM
users
WHERE
email = ?`;
var query_params = [emailAddress];
db.query(
query,
query_params,
function(error, result, fields) {
if (error) return reject(error)
if ( result.length == 1 ) {
if ( checkPass(password, result[0].password, result[0].salt ) ) {
resolve({id: result[0].id})
} else {
console.log("login False | Password");
reject();
}
} else {
console.log("login False | username");
reject();
}
}
)
})
}
You can learn more about Promise API here
EDIT:
So you should additionally make connect synchronous. Here's a piece of code I have refactored for you. It should work just fine. I have used some ES6 elements to make it more readable.
const connect = () => new Promise((resolve, reject) => {
db.connect((err) => {
if (err) return reject(err);
resolve();
})
})
const makeDbRequest = (emailAddress, password) => new Promise((resolve, reject) => {
const query = `
SELECT
id,
email,
password,
salt
FROM
users
WHERE
email = ?`;
const query_params = [emailAddress];
db.query(
query,
query_params,
handleDbData(resolve, reject, password),
);
})
const handleDbData = (resolve, reject, password) => (error, result, fields) => {
if (error) return reject(error)
if ( result.length == 1 ) {
if ( checkPass(password, result[0].password, result[0].salt ) ) {
resolve({id: result[0].id})
} else {
console.log("login False | Password");
reject();
}
} else {
console.log("login False | username");
reject();
}
}
module.exports = (emailAddress, password) => new Promise((resolve, reject) => {
connect()
.then(() => {
makeDbRequest(emailAddress, password)
.then(resolve)
.catch(reject)
})
.catch(reject);
})