Following is my getUser function
const getUsers = async (req, res) => {
try {
ddb.get({
TableName: "Tablename",
Key: { Username: req.query.username }
},(err,user) => {
if(err || Object.keys(user).length === 0) {
return res.status(404).json("Something went wrong")
} else {
const userSociety = Object.keys(user.Item.Societies)[0]
ddb.get({
TableName: "Tablename",
Key: { SocietyCode: userSociety }
},(err, society) => {
if(err || Object.keys(society).length === 0) {
return res.status(400).json({ message: "Could not fetch society members" })
} else {
const users = Object.keys(society.Item.SocietyMembers)
const usersData = []
users.forEach(async u => {
ddb.get({
TableName: "TestMyMohallaUsers",
Key: { Username: u }
},async (err,user) => {
if(err || Object.keys(user).length === 0) {
} else usersData.push({
Username: user.Item.Username,
Firstname: user.Item.Name
})
})
})
return res.status(200).json({ message: "Data detched successfully", Users: usersData })
}
})
}
})
} catch (error) {
return res.status(500).json({ error: "Internal Server Error" })
}
}
I want to wait for the execution of forEach and then send back the data via return statement but as of now the return statement gives empty array of users.
Clearly my code in not waiting for the execution of forEach and then returning the data. How can I do that someone help me?
Edit: ddb is an instance of DynamoDB
You'll have a better time if you
use the DynamoDB Promise API instead of a pyramid of callbacks
refactor your code to a couple of functions
Finally, awaiting for all user fetches to complete requires Promise.all for all of those promises.
async function getUser(ddb, username) {
const user = await ddb
.get({
TableName: "TestMyMohallaUsers",
Key: { Username: username },
})
.promise();
if (!user.Item) {
throw new Error(`User ${username} not found`);
}
return user.Item;
}
async function getSociety(ddb, societyCode) {
const society = await ddb
.get({
TableName: "Tablename",
Key: { SocietyCode: societyCode },
})
.promise();
if (!society.Item) {
throw new Error(`Society ${societyCode} not found`);
}
return society.Item;
}
const getUsers = async (req, res) => {
try {
const user = await getUser(ddb, req.params.username);
const userSocietyCode = Object.keys(user.Societies)[0];
const society = await getSociety(ddb, userSocietyCode);
const societyUsers = Object.keys(society.SocietyMembers);
const usersData = await Promise.all(
societyUsers.map(async (member) => {
const user = await getUser(ddb, member);
return {
Username: user.Username,
Firstname: user.Name,
};
}),
);
return res
.status(200)
.json({
message: "Data detched successfully",
Users: usersData,
});
} catch (e) {
return res
.status(400)
.json({ message: `Could not fetch information: ${e}` });
}
};
Related
Today I'm a little bit confused... I'm trying to get a list of posts of logged user by _id numbers included in user mongo document. When I call promise inside map method - I'm still receiving Array of Promise{}. I'm looking for info about this behavior but I don't understand why it behaves like this. It seems coded like it should be (in promise with async and await). getListOfThoughts method is created for posts download.
Thank you in advance
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
return await ThoughtModel.findOne({ _id: el });
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
console.log("new List", newListOfThoughts);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};
mongoose javascript mongodb async promise
I tried:
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
const post = await ThoughtModel.findOne({ _id: el });
return post;
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};
I am having a flow of registering a new user.
I am getting the error UnhandledPromiseRejectionWarning: TypeError: createUser is not a function
auth.js
const express = require("express");
const authrequests = express.Router();
const cors = require("cors");
var createUser = require("./export/authConstants");
// Register User
authrequests.post("/register", async (req, res) => {
const userData = {
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
phone: req.body.phone,
password: req.body.password,
created: new Date(),
};
await createUser(userData)
.then((res) => {
console.log(res)
if (res.status == 200) {
return res.status(200).json({ msg: 'Registered!' });
} else if (res.status == 405) {
return res.status(405).json({ error: 'User already exists' });
} else {
return res.status(400).json({ error: err });
}
})
.catch(err => {
return res.status(400).json({ error: err });
})
});
module.exports = authrequests;
authConstants.js
const customers = require("./../../models/customers");
var hashPassword = require("./util/bcrypt");
var jwtCreate = require("./util/jwt");
var sendMail = require("./util/mail");
var BASE_URL = require("./../constants/constants");
//register
createUser = (userData) => {
return new Promise(async (resolve, reject) => {
customers.findOne({ where: { email: userData.email } })
.then(async (user) => {
if (!user) {
var hashResponse = await hashPassword(userData.password)
if (hashResponse.msg) {
userData.password = hashResponse.msg
customers.create(userData)
.then(async (user) => {
if (user) {
var jwtResponse = await jwtCreate({ data: user.id, expiry: 172800 })
if (jwtResponse.msg) {
const url = `${BASE_URL}/auth/emailVerified/${jwtResponse.msg}`;
var mailResponse = await sendMail({
to: user.email,
subject: 'Email Verification',
html: `Click on the following link to verify your account: click here`
})
if (mailResponse.msg) {
resolve({ status: 200 })
} else {
reject({ error: mailResponse.err })
}
} else {
reject({ error: jwtResponse.err })
}
} else {
reject({ error: "oops..! user creation failed" })
}
})
.catch(err => {
reject({ error: err })
});
} else {
reject({ error: hashResponse.err })
}
} else {
resolve({ status: 405 })
}
})
.catch(err => {
reject({ error: err })
})
})
};
bcrypt.js
const bcrypt = require("bcrypt");
//hashing password
hashPassword = async (password) => {
await bcrypt.hash(password, 10, (err, hash) => {
if (hash) {
return { msg: hash };
} else {
return { error: err };
}
})
};
jwt.js
const jwt = require("jsonwebtoken");
var EMAIL_SECRET = require("./../../constants/constants");
//jwt creation
jwtCreate = async (data) => {
await jwt.sign(data.data, EMAIL_SECRET, { expiresIn: data.expiry }, (err, token) => {
if (token) {
return { msg: token };
} else {
return { error: err };
}
})
};
mail.js
const nodemailer = require("nodemailer");
var MAIL_HOST = require("./../../constants/constants");
var EMAIL_USER = require("./../../constants/constants");
var EMAIL_PASS = require("./../../constants/constants");
//mail send
sendMail = async (data) => {
let transporter = nodemailer.createTransport({
host: MAIL_HOST,
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: EMAIL_USER,
pass: EMAIL_PASS,
},
tls: {
rejectUnauthorized: false,
},
});
await transporter.sendMail({
from: EMAIL_USER,
to: data.to,
subject: data.subject,
html: data.html
}, (err, response) => {
if (token) {
return { msg: response };
} else {
return { error: err };
}
});
};
constants.js
const EMAIL_SECRET = "asdf1093KMnHGcvnkljvasdu09123nlasdasdf";
const MAIL_HOST = "mail.test.com";
const EMAIL_USER = "no_reply_auth#test.com";
const EMAIL_PASS = "JMkC+)*Lv";
const BASE_URL = "http://localhost:3001";
UnhandledPromiseRejectionWarning: TypeError: createUser is not a function
is there something I am missing out..? or the entire flow is wrong..?
Below is my function returning login token when I debug my function it waits until return but when call function returns undefined and errors are also undefined don't know why it's happening
import userModel from '../Models/user.model';
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken')
let token = null;
process.env.SECRET_KEY = 'secret';
export default class loginController{
static async login(user:any): Promise<any>{
try{
await userModel.findOne({
Email: user.Email
})
.then(async (res:any) => {
if (user) {
if (await bcrypt.compareSync(user.Password, res.Password)) {
const payload = {
Firstname: res.Firstname,
Lastname: res.Lastname,
email: res.Email,
}
token = await jwt.sign(payload, process.env.SECRET_KEY, {
expiresIn: 1400
})
let decoded = jwt.verify(token, process.env.SECRET_KEY)
return token;
}
else {
return "Password is Wrong";
}
}
else {
return 'Please Check Username';
}
})
.catch(err => {
return('error : ' + err)
})
}
catch(err)
{
return err
}
}
}
And my calling function is
const router : Router = Router();
router.post('/login', async (req, res, next) => {
try {
const user = await loginController.login(req.body);
res.json(user)
} catch (error) {
res.json(error)
}
})
I tried call errors it's also debugger waiting until returns value to error but showing undefined
Thanks for the help!
login function doesn't return token because of function scoping. If you have multiple callbacks you can wrap it with a new Promise and use resolve function for returning values.
export default class loginController {
static async login(user: any): Promise<any> {
try {
return new Promise(async (resolve, reject) => {
await userModel
.findOne({
Email: user.Email
})
.then(async res => {
if (user) {
if (await bcrypt.compareSync(user.Password, res.Password)) {
const payload = {
Firstname: res.Firstname,
Lastname: res.Lastname,
email: res.Email
};
const token = await jwt.sign(payload, process.env.SECRET_KEY, {
expiresIn: 1400
});
let decoded = jwt.verify(token, process.env.SECRET_KEY);
resolve(token);
} else {
resolve('Password is Wrong');
}
} else {
resolve('Please Check Username');
}
})
.catch(err => {
resolve('error : ' + err);
});
});
} catch (error) {
return error;
}
}
}
I created two model (I use MongoDB):
User: name, email, password, isBanned, blogPosts[].
BlogPost: title, content, date, author.
with author: {type:ObjectId, ref: 'User'}
I want insert a new blog post with Json Web Token. But I received error: : TypeError: verifyJWT is not a function
Here is my code:
const { verifyJWT } = require('./User')
const insertBlogPost = async (title, content, tokenKey) => {
try {
//Check Login with tokenKey
let signedInUser = await verifyJWT(tokenKey) //<-Code not run here
let newBlogPost = await BlogPost.create({
title, content,
date: Date.now(),
author: signedInUser
})
await newBlogPost.save()
await signedInUser.blogPosts.push(newBlogPost._id)
await signedInUser.save()
return newBlogPost
} catch(error) {
throw error
}}
module.exports = {
BlogPost,
insertBlogPost,
}
Here's verifyJWT from User
const jwt = require('jsonwebtoken')
const verifyJWT = async (tokenKey) => {
try {
let decodedJson = await jwt.verify(tokenKey, secretString)
if(Date.now() / 1000 > decodedJson.exp) {
throw "Token timeout, please Login again"
}
let foundUser = await User.findById(decodedJson.id)
if (!foundUser) {
throw "Can not find user with this token"
}
if(foundUser.isBanned === 1) {
throw "User looked"
}
return foundUser
} catch(error) {
throw error
}}
module.exports = { User, verifyJWT }
And here's my router:
router.post('/insertBlogPost', async (req, res) =>{
let {title, content} = req.body
//Client must send tokenKey
let tokenKey = req.headers['x-access-token']
try {
let newBlogPost = await insertBlogPost(title, content, tokenKey)
res.json({
result: 'ok',
message: 'Create BlogPost successfully!',
data: newBlogPost
})
} catch(error) {
res.json({
result: 'failed',
message: `${error}`
})
}})
Github: https://github.com/vantuan1303/authentication
I am working on a 'change password' functionality. I am starting to learn more about Promises and have the following code:
router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => {
if (!req.body.password_current || !req.body.password_new) {
req.flash('info', 'Please fill in both fields.');
return res.redirect('/change-password');
}
const data = {};
data.password = req.body.password_new;
tokenHandler.verifyToken(req.cookies.token)
.then((decoded) => {
return User.findOne({ '_id.user_id': decoded.user });
})
.then((user) => {
data.userId = ObjectId(user._id.user_id);
return bcrypt.compare(req.body.password_current, user.password);
})
.then((allowed) => {
if (!allowed) {
return res.redirect('/change-password');
}
console.log('I am not here');
return User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true });
})
.then(() => {
return res.redirect('/change-password');
})
.catch((err) => {
return next(err);
});
});
I love how Promises are preventing the 'callback hell'. The problem is that I am receiving a 'headers already sent' error. I know that is because I can't escape the chain and that it saves up all the results (unless you throw an Error). To fix the problem I used the following:
router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => {
if (!req.body.password_current || !req.body.password_new) {
req.flash('info', 'Please fill in both fields.');
return res.redirect('/change-password');
}
const data = {};
data.password = req.body.password_new;
tokenHandler.verifyToken(req.cookies.token)
.then((decoded) => {
User.findOne({ '_id.user_id': decoded.user }).then((user) => {
data.userId = ObjectId(user._id.user_id);
bcrypt.compare(req.body.password_current, user.password).then((allowed) => {
if (!allowed) {
return res.redirect('/change-password');
}
User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }).then((doc) => {
console.log(doc);
return res.redirect('/change-password');
});
});
});
});
});
The question is: Is there a better solution to fix the 'header already sent' error. Because I have the feeling that my solution is actually a few steps away from a 'callback hell' structure.
You can rewrite it like this
router.post('/change-password', verifyToken, csrfProtection, (req, res, next) => {
if (!req.body.password_current || !req.body.password_new) {
req.flash('info', 'Please fill in both fields.');
return res.redirect('/change-password');
}
const data = {};
data.password = req.body.password_new;
tokenHandler.verifyToken(req.cookies.token)
.then((decoded) => {
return User.findOne({ '_id.user_id': decoded.user });
})
.then((user) => {
data.userId = ObjectId(user._id.user_id);
return bcrypt.compare(req.body.password_current, user.password);
})
.then((allowed) => {
if (!allowed) {
return res.redirect('/change-password');
}
else{
console.log('I am not here');
return User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true })
.then(() => {
return res.redirect('/change-password');
});
}
})
.catch((err) => {
return next(err);
});
});
You can return a promise chain from within a then function.
Depending on your version of Node, you may also be able to re-write this using async / await. It generally makes things easier to reason about.
router.post('/change-password', verifyToken, csrfProtection, async (req, res, next) => {
if (!req.body.password_current || !req.body.password_new) {
req.flash('info', 'Please fill in both fields.');
return res.redirect('/change-password');
}
try {
const data = {};
data.password = req.body.password_new;
const decoded = await tokenHandler.verifyToken(req.cookies.token);
const user = await User.findOne({ '_id.user_id': decoded.user });
data.userId = ObjectId(user._id.user_id);
const allowed = await bcrypt.compare(req.body.password_current, user.password);
if (!allowed) {
return res.redirect('/change-password');
} else {
await User.findOneAndUpdate({ '_id.user_id': data.userId }, { password: data.password }, { new: true });
}
return res.redirect('/change-password');
} catch (err) {
return next(err);
}
});
You need Node.js >= 7 to use async/await.