How to chain mongoose query using promises? - javascript

Hey guys I'm beginner and I'm working on my first express and mongo application but I don't know how to chain queries with javascript promises to make it work as a sync query.
Please help me to chain these mongoose queries.
/* POST Register User */
router.post('/register',function(req,res,next){
let user = req.body;
//checking for empty field in a form
for(let key in user){
if(user[key] === ""){
return next(mid.error("All fields are required to fill"));
}
}
User.findOne({username:user.username})
.exec(function(err,user){
if(err){
return next(mid.error("Something went wrong"));
}
if(user){
return next(mid.error("Username already exist"));
}
});
User.findOne({email:user.email})
.exec(function(err,user){
if(err){
return next(mid.error("Something went wrong"));
}
if(user){
return next(mid.error("Email already exist"));
}
});
//matching password
if(user.password !== user.confirm){
return next(mid.error("Password not matched.Try again !"));
}
//save data in object
let userData = {
username : user.username,
email : user.email,
password : user.password
};
//save data in database
User.create(userData,function(err,user){
if(err){
return next(mid.error("Something went wrong.Try again !!!"));
} else {
req.session.userID = user._id;
return res.redirect('/home');
}
});
});

/* POST Register User */
router.post('/register',function(req,res,next){
let user = req.body;
let validError = [];
//checking for empty field in a form
for(let key in user){
if(user[key] === ""){
return next(mid.error("All fields are required to fill"));
}
}
let findUserName = () => {
return new Promise(function(resolve,reject){
User.findOne({username:user.username})
.exec(function(err,user){
if(err){
validError.push("Something went wrong");
}
if(user){
validError.push("Username already exist");
}
return resolve();
});
});
}
let findUserEmail = () => {
return new Promise(function(resolve,reject){
User.findOne({email:user.email})
.exec(function(err,user){
if(err){
validError.push("Something went wrong");
}
if(user){
validError.push("Email already exist");
}
return resolve();
});
});
}
let isPasswordMatch = () => {
return new Promise(function(resolve,reject){
if(user.password !== user.confirm){
validError.push("Password not matched");
}
return resolve();
});
}
findUserName().then(findUserEmail).then(isPasswordMatch).then(() => {
if(validError.length == 0){
//save data in object
let userData = {
username : user.username,
email : user.email,
password : user.password
};
//save data in database
User.create(userData,function(err,user){
if(err){
return next(mid.error("Something went wrong.Try again !!!"));
} else {
req.session.userID = user._id;
return res.redirect('/home');
}
});
}else{
return next(mid.error(validError[0]));
}
});
});
Finally did my own ! Thanks all of you

Promise chaining is't always favourable, you have to analyse whether chaining is going to support your situation or will worsen your problem. In your case if any of the query { findByName, findByEmail, Create} fails you have to return back from there. If you try it with promise chaining then if any of the above operations fails even then the next .then or .catch of chained promises will get executed successively. So in my point of view all you need is Promise.all. Below I have provided the link of the code on jsfiddle. For your understanding I have given chaining approach but that is not recommended. Just go with Promise.all
with Promise.all
with promises chaining

Use Rx, and mongoose-observables :)
var Rx = require('rx');
var observables = require('mongoose-observables');
/* get public header data, list of collections and projects */
router.get("/header", function(req, res) {
let articleTitle = observables.finder.find(Articles, '{}', ['title'], null);
let collectionTitle = observables.finder.find(Collections, '{}', ['title'], null);
Rx.Observable.forkJoin([articleTitle, collectionTitle])
.subscribe(
(docs) => res.json(docs),
(err) => console.error(err)
);
});
More example at https://www.npmjs.com/package/mongoose-observables

Related

Unable to access user info object property - facebook chat api

I'm using Facebook chat api to create a simple cli script that will reply to messages that are sent to my facebook account. I'm trying to assign and get the user name and my name to use them inside the reply but they are always undefined. I think that the object property aren't assigned correctly. Is there a fix for this?
require('dotenv').config();
const fs = require('fs');
const fb = require('facebook-chat-api');
const path = require('path');
const appStateFile = path.format({ dir: __dirname, base: 'appstate.json' });
let currentUser = null;
if( !fs.existsSync(appStateFile) ){
//debug .env
console.log(process.env);
fb({email: process.env.FB_EMAIL, password: process.env.FB_PWD}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
fs.writeFileSync(appStateFile, JSON.stringify(api.getAppState()));
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile); // profile is logged correctly
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user); // user object is logged correctly
api.sendMessage('...', event.threadID)
});
}
});
});
}else{
fb({appState: JSON.parse(fs.readFileSync(appStateFile))}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile);
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user)
api.sendMessage(`FB Pager v1.0.\nHi ${user.name}!Your message was forwarded with an email to ${currentUser.name}.`, event.threadID)
});
}
});
});
}
I think the problem here is that api.getUserInfo is asynchronous.
So you would need to nest them to get it to work.
Or you can try this, since getUSerInfo allows you to add an array of user ids to get the data for:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
const currentUserId = api.getCurrentUserID();
const senderId = event.senderID;
api.getUserInfo([currentUserId, senderId], (err, ret) => {
if(err) return console.error(err);
// Ret should contain the two users
// See: https://github.com/Schmavery/facebook-chat-api/blob/master/DOCS.md#getUserInfo
console.log(ret);
});
}
});
Nesting user calls method:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
let currentUserId = api.getCurrentUserID();
api.getUserInfo(currentUserId, (err1, signedInUser) => {
if (err1) {
return console.log(err);
}
api.getUserInfo(event.senderID, (err2, userInMessage) => {
if (err2) {
return console.log(err);
}
console.log(signedInUser, userInMessage)
api.sendMessage("...", event.threadID);
});
});
}
});
After a lot of debug I've found the correct way to access the needed informations. Since the user informations after that are retrived are mapped to another object that is the userId, the only way to access to each property is to use a for loop. Initially I was thinking that this can be avoided but unfortunately it's necessary otherwise using only dot notation will result in undefined. This is how I've solved
api.getUserInfo(userId, (err, user) => {
let username;
if(err){
return console.log(err);
}
for(var prop in user){
username = user[prop].name;
}
api.sendMessage(`Hello ${username!}`, event.threadID);
});

How do I get this function to run synchronously?

async await is very confusing. I have the below code and here I am trying to implement a create user function that first needs to check if the user exists in my MySQL database. I have the initial setting of the user where I call await on my findByUsername function. However, The function finishes after my if(!user) code has been executed.
I want the program to wait for the findByUsername function to finish and then perform the check on whether the user exists or not.
const { username, password, firstname, permission } = req.body;
let user = await User.findByName(req.body.username, (err, data) => {
if (err) {
if (err.kind === "not_found") {
console.log("finished");
return null;
} else {
console.error("error occurred");
}
} else {
console.log("finished too");
return data;
}
});
if (!user) {
console.log("couldnt find user");
res.status(404).send("couldnt find it :(");
} else {
console.log("found user");
res.send("found them");
}
===EDIT===
I am getting another that has also been confusing me where it says that result is not a function inside of my findByName function on my User model.
sql.query(`SELECT * from users WHERE username = '${username}'`, (err, res) => {
if (err) {
console.log("error ", err);
result(err, null);
return;
}
if (res.length) {
console.log("found user: ", res[0]);
result(null, res[0]);
return;
}
console.log("couldnt find");
return { kind: "not_found" }, null;
});
};
We must use await inside an async function.
For example:
const getUser = async (username) => await User.findByName(username);
Then call that function inside a try catch
try {
user = getUser(someUsername)
} catch (error) {
// Handle errors here
}
// Code here will only run once getUser is finished
Since i don't know your UserService i don't know if "User.FindByName" is a async function. but if that is the case you could do it like this:
const { username, password, firstname, permission } = req.body;
try {
let user = await User.findByName(req.body.username);
if (!user) {
console.log("couldnt find user");
res.status(404).send("couldnt find it :(");
} else {
console.log("found user");
res.send("found them");
}
} catch (e) {
res.status(400).send(e);
}
Are you able to change the definition of User.findByName?
Instead of using await and also passing in a callback, why not remove the callback and process it synchronously?
let user = await User.findByName(username);
// Handle response here
this will solve your Problem If you are using sequelize with mysql you can use where
condition in your code so you can use more conditions in it
const { username, password, firstname, permission } = req.body;
try {
let user = await User.findOne({
where: { username: req.body.username },
attributes: { exclude: ['password', 'signup_token'] }
});
if (!user) {
console.log("couldnt find user");
res.status(401).send("couldnt find user with username" +
(req.body.username));
} else {
console.log("User Found");
return res.status(200).send("User Found With Username" +
(req.body.username));
}
} catch (error){
console.log(error);
return res.status(500).send(error);
}
The function finishes after my if(!user) code has been executed.
That is because you are mixing async/await with callbacks which you should not.
// Here you are mixing async/await and callbacks
let user = await User.findByName(req.body.username, (err, data) => { /*....*/ });
// it is the same as
function findByNameCallback(err, data) { /*.....*/ }
let user = await User.findByName(req.body.username, findByNameCallback);
// -- So you either remove the callback from the equation
var user;
try {
user = await User.findByName(req.body.username);
} catch (err) {
// Handle error
}
// Work with the user object...
// ----
// Or remove the async/await stuff
User.findByName(req.body.username, findByNameCallback);

Express + login script

My login script was working with Postman, but as soon as I attempted to get React.js to post it to the server it started breaking.
The issue is that, when it checks for the user, it is not able to pass back the return result For some reason it just does not do anything.
Login route:
router.post('/login', async (req,res) => {
console.error(req.body.password)
try {
var check = await checkUser(req.body.email, req.body.password).catch((result) => {
console.log(result)
})
} catch(err){
console.error(err)
}
});
Check function:
async function checkUser(username, password) {
var username = username;
var password = password;
var result;
await Users.findOne({email: username}, async function (err, docs) {
// console.error(docs);
if (err) {
console.log(err)
} else {
const match = await bcrypt.compare(password, docs.password);
if(match) {
result = {userid:docs._id, success:"true"}
//result['userid'] = docs._id;
//result['success'] = true;
} else{
result = {success:"false"}
// result['success'] = false;
}
}
// console.error(result);
return result;
})
}
Wondering if anyone can see where I am going wrong.
Dont use .catch() if you go with async / await. To catching errors you already have try / catch
router.post('/login', async (req,res) => {
console.error(req.body.password)
try {
var check = await checkUser(req.body.email, req.body.password)
} catch(err){
console.error(err)
}
});
Down there similar problem. You use the callback approach with async / await. The reason why you doesnt get anything back is that you returning your result to your callback function ...async function().. if you take a closer look.
However as i said, dont use callback + async / await
Remember use either: async / await or chaining .then() .catch() or use the callback approach but not all together its bad and leads to unessecary bugs.
async function checkUser(username, password) {
var username = username;
var password = password;
var result;
try {
let user = await Users.findOne({ email: username });
if (!user) return "no user found";
const match = await bcrypt.compare(password, user.password);
if (match) {
result = { userid: user._id, success: "true" };
} else {
result = { success: "false" };
}
return result;
} catch (err) {
console.log(err);
}
}

Async / await in node nested functions?

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);
})

Static Promise.resolve()/reject() is always interpreted as resolve()

I'm using the following two pieces of code :
Store.addUser(newUserInfo).then(function(firstResult) {
Store.getUserList().then(function(result){
console.log('this side');
console.log(result);
io.sockets.emit('userAdded', {
userMapByUserId: result
});
}, function(error) {
console.log('List of users could not be retrieved');
console.log(error);
io.sockets.emit('userAdded', {
userMapByUserId: []
});
}
);
}, function(rejection) {
socket.emit('userNotAdded', {
userId: -1,
message: rejection.reason,
infoWithBadInput: rejection.infoWithBadInput
});
});
and in Store :
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
return keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName'
};
return Promise.reject(validationResult);
} else {
var newUserId = generateUserId();
//TODO: change it somehting more flexible. e.g. a predefined list of attributes to iterate over
var newUser = {
'userName': newUserInfo.userName,
'password': newUserInfo.password,
'userId': newUserId,
'lastModificationTime': Common.getCurrentFormanttedTime(),
'createdTime': Common.getCurrentFormanttedTime()
};
var user = new User(newUser);
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
return Promise.reject('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
//return getUserList();
return Promise.accept(newUser);
}
});
}
});
} else {
return Promise.reject(validationResult);
}
};
But in the first code , when I do Store.addUser(newUserInfo) it always runs the first function (resolve function) which shouldn't be the case if we do return Promise.reject() in addUser. Any idea on why this happens ?
You've got two return statements too few, two too much, and are overlooking a non-promisified function call.
Store.addUser(newUserInfo).then(function(firstResult) {
return Store.getUserList().then(function(result){
// ^^^^^^
…
This one is not really problematic, as you don't chain anything after the resulting promise, but it shouldn't be missed anyway.
…
return keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
…
} else {
…
var user = new User(newUser);
user.save(function(err) { … });
// ^^^^
}
});
In this then-callback, you are not returning anything from your else branch. The promise is immediately fulfilled with undefined, and the ongoing save call is ignored - your promises don't know about it, so they can't await it. That's why Store.getUserList() that follows next in the chain doesn't see the changes; they're not yet stored.
It's also the reason why your Promise.reject inside that callback is ignored, and why Promise.accept never caused any problems.
You will need to create a new promise for the result of the save invocation here (so that you actually can return it):
…
var user = new User(newUser);
return new Promise(function(resolve, reject) {
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
reject('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
resolve(newUser);
}
});
}); // .then(getUserList);

Categories

Resources