I need to register a new user, when receiving the parameters make a query using the city name to get the state and city id (both are foreign keys). I implemented a function to find the ids. Inside the function using data.id the id is returned correctly. But at the time of insert in database is being inserted "undefined".
Apparently the save operation is being executed before the findCity and findState functions return the value.
execution flow
cidade = city, estado = city
module.exports = app => {
const obterHash = (senha, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(senha, salt, null, (err, hash) => callback(hash))
})
}
var idCidade;
var idEstado
function findCidade(cidade, ) {
app.db('cidades')
.where({ nome: cidade })
.first()
.then(data => {
idCidade = data.id
console.log('inside findCity. data.id: '+data.id)
}).catch((err) => console.log("erro cidade", err));
return
}
function findEstado(uf) {
app.db('estados')
.where({ uf: uf })
.first()
.then(data => {
idEstado = data.id
console.log('inside findState. data.id: '+data.id)
}).catch((err) => console.log("erro estado", err));
}
const save = (req, res) => {
console.log("\n")
findCidade(req.body.cidade)
findEstado(req.body.uf)
obterHash(req.body.senha, hash => {
const senha = hash
console.log("Will be inserted. idCity: "+idCidade+" idState: "+idEstado)
app.db('salao')
.insert({ idcidade: idCidade,
idestado: idEstado,
senha})
.then(_ => res.status(200).send())
.catch(err =>{res.status(400).json(err)})
})
}
return { save }
}
I'm from Brazil and I'm using a translator, sorry for the spelling mistakes.
You are welcome to the asynchronous world!
General explanation: You are going to use results of a database querying before it will happen. Your program have to wait the results (idCidade, idEstado) before you can use it. Because of it you can find the record Will be inserted... first in your logs.
For the explanation I'm going to use Minimal Reproducible Example.
function findCidade(cidade) {
return Promise.resolve(1);
}
function findEstado(uf) {
return Promise.resolve(1);
}
Promise.all([findCidade(), findEstado()])
.then((data) => console.log(data));
The output is:
[ 1, 1 ]
To solve the issue you have to:
Return the promise explicitly with return statement.
Await the results by async/await or Promise interface methods. Or use callbacks if it is more suitable to you.
module.exports = app => {
const obterHash = (senha, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(senha, salt, null, (err, hash) => callback(hash))
})
};
function findCidade(cidade, ) {
return app.db('cidades')
.where({ nome: cidade })
.first()
.then(data => {
idCidade = data.id
console.log('inside findCity. data.id: '+data.id)
}).catch((err) => console.log("erro cidade", err));
}
function findEstado(uf) {
return app.db('estados')
.where({ uf: uf })
.first()
.then(data => {
idEstado = data.id
console.log('inside findState. data.id: '+data.id)
}).catch((err) => console.log("erro estado", err));
}
const save = (req, res) => {
console.log("\n");
Promise.all([findCidade(req.body.cidade), findEstado(req.body.uf)])
.then((data) => {
const [idCidade, idEstado] = data;
obterHash(req.body.senha, hash => {
const senha = hash;
console.log("Will be inserted. idCity: "+idCidade+" idState: "+idEstado);
app.db('salao')
.insert({ idcidade: idCidade,
idestado: idEstado,
senha})
.then(_ => res.status(200).send())
.catch(err =>{res.status(400).json(err)})
})
})
.catch((err) => console.log("general error", err));
};
return { save }
}
Related
So far I am building an app using express and firebase cloud functions. I am not able to create a nested json according to this db:
Here is the code:
exports.tstMenu = (req, res) => {
let shop = db.collection('shops').doc(req.params.name).collection('menus');
shop.get()
.then((data) => {
let menu = [];
data.forEach((doc) => {
let categories = getCategories(doc.id, shop);
menu.push({
menuID: doc.id,
name: doc.data().name,
position: doc.data().position,
categories: categories,
});
console.log(menu);
});
return res.json(menu);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.message});
});
}
function getCategories(id, db){
let shop = db.doc(id).collection('categories');
return shop.get()
.then((data) => {
let categs = [];
data.forEach((doc) => {
var menuElements = [];//getMenuElement(doc.id, shop);
categs.push({
catID: doc.id,
name: doc.data().name,
position: doc.data().position,
menuElements: menuElements,
});
});
return categs;
});
}
and the result of tstMenu is:
while the log is showing this:
Can anyone explain me how to fix it? I am quite sure that promises are not received when tstMenu reaches return res.json(menu);
Your problem lies within this line :
let categories = getCategories(doc.id, shop);
getCategories is an async method. It returns a promise so you can't use it directly as you do.
You either should do your assignment in a then callback or you should use async await.
exports.tstMenu = (req, res) => {
let shop = db.collection('shops').doc(req.params.name).collection('menus');
shop.get()
.then((data) => {
let menu = [];
const promises = data.docs.map((doc) => // change this from forEach to map
getCategories(doc.id, shop).then(categories =>{
menu.push({
menuID: doc.id,
name: doc.data().name,
position: doc.data().position,
categories: categories,
});
);
return Promise.all(promises).then(()=> res.json(menu)); // return res after all promises completed
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.message});
});
}
Or
exports.tstMenu = async (req, res) => {
try {
let shop = db.collection('shops').doc(req.params.name).collection('menus');
const data = await shop.get()
let menu = [];
const promises = data.docs.map((doc) => // change this from forEach to map
getCategories(doc.id, shop).then(categories =>{
menu.push({
menuID: doc.id,
name: doc.data().name,
position: doc.data().position,
categories: categories,
});
);
await Promise.all(promises);
return res.json(menu)
} catch(err) {
console.error(err);
return res.status(500).json({ error: err.message});
}
}
I'm using MySQL2 to connect my Node.js app with a MySQL Database.
Unfortunately trying to perform some promise based prepared statements I just can't get a proper function setup that either returns successfully after entering the record or to throw an error whenever something goes wrong.
Any ideas on how to fix the code below?
// Connection Settings
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
port: process.env.DB_PORT
})
// Promise based SQL Prepared Statement
db.pps = ({ query, variables, error }) => {
return new Promise((resolve, reject) => {
connection.execute(query, variables, (err, results) => {
if (err) {
console.log(`Error: ${error} \n ${err.sqlMessage}`)
return reject(err)
}
return resolve(results)
})
})
}
// Sign Up
auth.signup = (req, res) => {
const query = `
INSERT INTO User (Id, Email, Password)
VALUES (UUID_TO_BIN(UUID()), ?, ?)
`
const variables = [req.query.email, req.query.password]
db.promise({ query, variables }, (err, result) => {
if (err) {
res.status(400)
}
res.status(200)
})
}
you can use the prepared statement query function like below.
If you are not using this inside a function
auth.signup = (req, res) => {
const query = `
INSERT INTO User (Id, Email, Password)
VALUES (UUID_TO_BIN(UUID()), ?, ?)
`
const variables = [req.query.email, req.query.password]
db.pps({ query, variables })
.then(result => {
res.status(200)
})
.catch( error => {
res.status(400)
});
}
Or use async await
auth.signup = async (req, res) => {
const query = `
INSERT INTO User (Id, Email, Password)
VALUES (UUID_TO_BIN(UUID()), ?, ?)
`
const variables = [req.query.email, req.query.password]
try {
await db.pps({ query, variables });
res.status(200)
} catch (err) {
res.status(400)
}
}
You have to do 2 changes:
1.Remove error param
db.pps = ({ query, variables}) => {
2. Change the SignUp code as below
auth.signup = (req, res) => {
const query = `
INSERT INTO User (Id, Email, Password)
VALUES (UUID_TO_BIN(UUID()), ?, ?)
`
const variables = [req.query.email, req.query.password]
db.pps({ query, variables }).then(response => {
// Do stuff with users
})
.catch(err => {
// handle errors
})
function pps( query, variables){
return new Promise((resolve, reject) => {
connection.execute(query, variables, (err, results) => {
if (err) {
console.log(`Error: ${error} \n ${err.sqlMessage}`)
reject(err)
}
resolve(results)
})
})
}
auth.signup = (req, res) => {
const query = `
INSERT INTO User (Id, Email, Password)
VALUES (UUID_TO_BIN(UUID()), ?, ?)
`
const variables = [req.query.email, req.query.password]
pps(query, variables)
.then(result => {
res.status(200)
})
.catch( error => {
res.status(400)
});
}
How to do promise with forEach? I want to get all jobs, but get the data of applicants. My Job schema already have the applicant id, but how to query the user and merge their detail in the output?
Job.find({}).then(result => {
result.forEach(obj =>{
const applicant_id = obj.applicant._id
if(applicant_id){
User.findOne({_id: applicant_id})
.then(user=>{
return res.json({
status: 1,
data: {
...obj,
applicant: {
...user
}
}
})
})
}
})
}).catch(err => {
if(err){
return res.status(400).send({
msg: err
})
}
})
I tried Promise but I'm stuck merging user into the Job obj,
Job.find({}).then(result => {
let promiseArray = []
result.forEach(obj =>{
const applicant_id = obj.applicant._id
if(applicant_id){
promiseArray.push(
User.findOne({_id: applicant_id}))
}
})
return Promise.all(promiseArray)
}).then(user => {
console.log(user)
//this work but this is only the user's data,
//I need it to be within obj which is Job data
})
You first need to filter items in result to exclude those without applicant id, then map this array to array of promises, and finally pass it to Promise.all. This should do it:
Job.find({}).then(result => {
const promises = result
.filter(obj => obj.applicant._id)
.map(obj => {
const applicant_id = obj.applicant._id
return User.findOne({ _id: applicant_id })
.then(user => {
return res.json({
status: 1,
data: {
...obj,
applicant: {
...user
}
}
})
})
})
return Promise.all(promises)
}).catch(err => {
if (err) {
return res.status(400).send({
msg: err
})
}
})
Here's a tested and working solution:
Job.find({ applicant: { $ne: null } }).populate('applicant').then(result => {
res.send(result);
}).catch(err => {
return res.status(400).send({
msg: err
})
});
I am not sure how to pass data to a promise function like below.
I need to parss it a JSON object that is then used in my MSSQL query, but if i remove the function around the promise, it says that data is undefined.
The code below is functional, I am just looking for a cleaner way to do this.
routes.post('/save', function(req, res){
var insert = function(data) {
sql.connect(config)
.then(pool => {
return pool.request()
.input('first_name', sql.VarChar(100), data.firstName)
.input('last_name', sql.VarChar(100), data.lastName)
.query('INSERT INTO Uncomplete_registration (first_name, last_name) VALUES (#first_name, #last_name)')
}).then(result => {
console.dir(result)
}).catch(err => {
console.dir(err)
})
sql.on('error', err => {
console.dir("other error: " + err);
})
}
insert(req.body.data);
});
I am sure there is a better way to do this but I am not sure how...
Try this
routes.post('/save', function(req, res){
var data = req.body.data;
sql.connect(config)
.then(pool => {
return pool.request()
.input('first_name', sql.VarChar(100), data.firstName)
.input('last_name', sql.VarChar(100), data.lastName)
.query('INSERT INTO Uncomplete_registration (first_name, last_name) VALUES (#first_name, #last_name)')
}).then(result => {
console.dir(result)
}).catch(err => {
console.dir(err)
})
sql.on('error', err => {
console.dir("other error: " + err);
})
});
This makes data into a local variable, which is essentially what your function is doing. The promise .then/.catch can then access it as a closure variable.
routes.post("/save", function (req, res) {
var data = req.body.data;
sql.connect(config)
.then(pool => {
return pool.request()
.input("first_name", sql.VarChar(100), data.firstName)
.input("last_name", sql.VarChar(100), data.lastName)
.query("INSERT INTO Uncomplete_registration (first_name, last_name) VALUES (#first_name, #last_name)");
}).then(result => {
console.dir(result);
}).catch(err => {
console.dir(err);
});
sql.on("error", err => {
console.dir("other error: " + err);
});
});
I want to reward a user when he undertakes an action. It can happen the path to his 'coins' does not exists yet. That is why I get the error:
Transaction failure: Error: The data for XXX does not exist.
How can I run a transaction while the path can not exist yet? This is what I tried:
exports.facebookShared = functions.firestore.document('facebookShared/{randomUID}').onCreate(event => {
const data = event.data.data()
const uid = data.uid
var promises = []
promises.push(
db.collection('facebookShared').doc(event.data.id).delete()
)
const pathToCoins = db.collection('users').doc(uid).collection('server').doc('server')
promises.push(
db.runTransaction(t => {
return t.get(pathToCoins)
.then(doc => {
var newCoins = 0
if (doc.data().hasOwnProperty("coins")){
newCoins = doc.data().coins + awardFacebookShare.coins
}
t.update(pathToCoins, { coins: newCoins });
});
})
.then(result => {
console.log('Transaction success', result);
})
.catch(err => {
console.log('Transaction failure:', err);
})
)
return Promise.all(promises)
})
I came across this docs: https://cloud.google.com/nodejs/docs/reference/firestore/0.8.x/Firestore#runTransaction
That docs are better than here: https://firebase.google.com/docs/firestore/manage-data/transactions
Below code works:
exports.facebookShared = functions.firestore.document('facebookShared/{randomUID}').onCreate(event => {
const data = event.data.data()
const uid = data.uid
var promises = []
promises.push(
db.collection('facebookShared').doc(event.data.id).delete()
)
const pathToCoins = db.collection('users').doc(uid).collection('server').doc('server')
promises.push(
db.runTransaction(t => {
return t.get(pathToCoins)
.then(doc => {
var newCoins = awardFacebookShare.coins
if (doc.exists){
if (doc.data().hasOwnProperty("coins")){
newCoins += doc.data().coins
}
t.update(pathToCoins, { coins: newCoins });
return Promise.resolve(newCoins);
}else{
t.create(pathToCoins, { coins: newCoins });
return Promise.resolve(newCoins);
}
});
})
.then(result => {
console.log('Transaction success', result);
})
.catch(err => {
console.log('Transaction failure:', err);
})
)
return Promise.all(promises)
})