trying to get single item from list - javascript

I have a fairly bare bones mern stack and im trying to call getUsers and then retrieve a single user from the returned list of users.
however using [] doesnt seem to work. It looks like getUsers correctly returns the list of users but idk how to pull a single one out
user-ctrl.js
const User = require('../models/user-model')
createUser = (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a user',
})
}
const user = new User(body)
if (!user) {
return res.status(400).json({ success: false, error: err })
}
user
.save()
.then(() => {
return res.status(201).json({
success: true,
id: user._id,
message: 'User created!',
})
})
.catch(error => {
return res.status(400).json({
error,
message: 'User not created!',
})
})
}
updateUser = async (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a body to update',
})
}
User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(404).json({
err,
message: 'User not found!',
})
}
user.name = body.name
user.email = body.email
user
.save()
.then(() => {
return res.status(200).json({
success: true,
id: user._id,
message: 'User updated!',
})
})
.catch(error => {
return res.status(404).json({
error,
message: 'User not updated!',
})
})
})
}
deleteUser = async (req, res) => {
await User.findOneAndDelete({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUserById = async (req, res) => {
await User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUsers = async (req, res) => {
await User.find({}, (err, users) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!users.length) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: users })
}).catch(err => console.log(err))
}
module.exports = {
createUser,
updateUser,
deleteUser,
getUsers,
getUserById,
}

You need to actually call the getUsers function (with parenthesis), and then wait for the promise to resolve, with await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
or
UserCtl.getUsers()
.then(u=>u[0])
.then(user=>{
// insert code that uses the user here
})

It's a promise, so try with async/await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
To make await work, put async infront of your method:
async createUser = (req, res) => {

Related

Why am I getting Promise Pending despite using await in my controller?

I have a repository where I connect directly to my model to insert some data, it creates the data successfully but when I connect my controller to this repository, I get a nulled response, if I log it in the repository itself I get Promise . Please checkout my code below:-
Repository.js
exports.register = (request) => {
const data = UserModel.findOne({email: request.email})
.then(user => {
if(user)
{
return {status: 400, message: 'Email Already exist'}
} else {
return bcrypt.genSalt(10, (err, salt) => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
return bcrypt.hash(newUser.password, salt, async (err, hash) => {
if(err) throw err;
newUser.password = hash;
return newUser.save()
.then(user => {
const token = jwt.sign({id: user._id}, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return {status: 200, message: 'Successfully Registered', auth: true, token: token, user: user}
})
.catch(err => {
return {status: 400, message: err}
})
})
})
}
})
console.log(data) // This part is return Promise <pending>
return data;
};
Controller.js
exports.SeedRegisteration = async (req, res, next) => {
try {
let element = await userRepo.register({username: "Testin", email: "Testin#test.com", "password":
"joe" });
return await res.status(200).json({ status: 200, data: element })
} catch (e) {
return res.status(400).json({ status: 400, message: e.message });
}
};
Works fine but does not return data
Here's the register function using the Promise version of bcrypt (if you don't supply a callback, the bcrypt functions return a Promise
exports.register = (request) =>
UserModel.findOne({
email: request.email
})
.then(user => {
if (user) {
throw 'Email Already exist'
}
})
.then(() => bcrypt.genSalt(10))
.then(salt => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
return bcrypt.hash(newUser.password, salt)
.then((hash) => {
newUser.password = hash;
return newUser.save();
})
}).then(user => {
const token = jwt.sign({
id: user._id
}, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return {
status: 200,
message: 'Successfully Registered',
auth: true,
token: token,
user: user
}
}).catch(err => {
return {
status: 400,
message: err
}
});
Note: there is ONE nested .then - this code could be perfectly flat if you used async/await in register - however I was not prepared to perform such a big rewrite for the answer. Now that the code is in a nice almost flat promise chain, it's relatively simple to convert the whole thing into async/await style
There are too many return statements which return promise. Please update your code in to the following:
exports.register = (request) => {
return new Promise((resolve, reject) => {
try {
UserModel.findOne({ email: request.email })
.then(user => {
if (user) {
return reject({ status: 400, message: 'Email Already exist' })
} else {
bcrypt.genSalt(10, (err, salt) => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
bcrypt.hash(newUser.password, salt, async (err, hash) => {
if (err) return reject(err);
newUser.password = hash;
newUser.save()
.then(user => {
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return resolve({ status: 200, message: 'Successfully Registered', auth: true, token: token, user: user })
})
.catch(err => {
return reject({ status: 400, message: err })
})
})
})
}
}).catch(err => {
return reject(err)
})
} catch (error) {
return reject(error)
}
});
};

Object is returned the same although I'm mutating it

The upcoming code snippet is removing the password attribute from the user JSON object and return it in response. what is happening is that the password attribute is still returning!
const signin = (req, res, next) => {
let requestBody = req.body;
userModel.findUserByEmail(requestBody.email).then(user => {
bcrypt.compare(requestBody.password, user.password, (error, result) => {
if (!result) {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
}
if (error) {
return res.status(500).json({
error
});
}
let token = jwt.sign({
email: user.email,
userId: user._id
},
process.env.JWT_KEY,
{
expiresIn: "2h"
});
// remonve password key
delete user.password
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token,
user
}
});
});
}).catch(error => {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
});
}
not sure the problem is related to async compilation or not
You could create a new object without the password and use that in your response:
const { password, ...restOfUser } = user
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token
user: restOfUser
}
})

How can i make this an async function

When i run my below code i get the error that await is only supported in an async function. How can i make the below async as my route is async but i guess because i am calling a promise within my async function i need make that async to. Below is the route
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(result=>{
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}).catch(err=>{
res.json({ Success: false , Error: err, Message: ""})
})
})
Try this:
contactRoutes.get('/:id', async(req, res) => {
try{
let result = await cb.getDoc(req.bucket, req.params.id );
let tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}
catch(err){
res.json({ Success: false , Error: err, Message: ""})
}
})
You have to pass a aync callback into your promise resolver then like this,
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(async (result)=>{
...........
}
})
or you may also use await in your promise call like this,
contactRoutes.get('/:id', async (req, res) => {
var result = await cb.getDoc(req.bucket, req.params.id)
var tasks = await cb.n1qlQuery_wId(req.bucket, req.N1qlQuery, cbQ.qContactTasks, req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true, Error: "", Message: "", Data: result.value })
}).catch(err => {
res.json({ Success: false, Error: err, Message: "" })
});
Make the callback function in .then async. See code below.
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(async result=>{
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}).catch(err=>{
res.json({ Success: false , Error: err, Message: ""})
})
})
you have add async to then(...)'s handler AND not the route. Why? Since, await is used in that handler.
You can do this:
contactRoutes.get('/:id', async(req, res) => {
try {
const result = await cb.getDoc(req.bucket, req.params.id )
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
} catch (err) {
res.json({ Success: false , Error: err, Message: ""})
}
})

API testing using postman

I am developing Rest APIs for some project and testing them using postman to send the data on my mLab server. But All I could get:
{
"error": {
"message": "ENOENT: no such file or directory, open 'C:\\Users\\Admin\\Desktop\\periodical API\\uploads\\2018-06-16T14:34:38.384Zhd-wallpaper-of-live.jpg'"
}
}
Here's my route code:
const mongoose = require("mongoose");
const Product = require("../models/product");
exports.products_get_all = (req, res, next) =>
{
Product.find()
.select("name price quantity date subject _id productImage")
.exec()
.then(docs => {
const response = {
count: docs.length,
products: docs.map(doc => {
return {
name: doc.name,
price: doc.price,
quantity: doc.quantity,
date: doc.date,
subject: doc.subject,
productImage: doc.productImage,
_id: doc._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + doc._id
}
};
})
};
res.status(200).json(response);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_create_product = (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
quantity: req.body.quantity,
date: req.body.date,
subject: req.body.subject,
productImage: req.file.path
});
product
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Created product successfully",
createdProduct: {
name: result.name,
price: result.price,
quantity: result.quantity,
date: result.date,
subject: result.subject,
_id: result._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + result._id
}
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_get_product = (req, res, next) => {
const id = req.params.productId;
Product.findById(id)
.select("name price quantity date subject _id productImage")
.exec()
.then(doc => {
console.log("From database", doc);
if (doc) {
res.status(200).json({
product: doc,
request: {
type: "GET",
url: "http://localhost:3000/products"
}
});
} else {
res
.status(404)
.json({ message: "No valid entry found for provided ID" });
}
})
.catch(err => {
console.log(err);
res.status(500).json({ error: err });
});
};
exports.products_update_product = (req, res, next) => {
const id = req.params.productId;
const updateOps = {};
for (const ops of req.body) {
updateOps[ops.propName] = ops.value;
}
Product.update({ _id: id }, { $set: updateOps })
.exec()
.then(result => {
res.status(200).json({
message: "Product updated",
request: {
type: "GET",
url: "http://localhost:3000/products/" + id
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_delete = (req, res, next) => {
const id = req.params.productId;
Product.remove({ _id: id })
.exec()
.then(result => {
res.status(200).json({
message: "Product deleted",
request: {
type: "POST",
url: "http://localhost:3000/products",
body: { name: "String", price: "Number" }
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
I myself could not figure out the problem as I am a bit newbie on developing APIs.
On Linux servers ENOENT means
“No such file or directory”
said that the response you are getting is trying to let you know that,
The directory where you are trying to save, does not exist
The place of file you are looking for does not exist.
What I do recommend you is that you use your debugger tool to stop the execution before you try to get to the directory or where you try to read your file. That way you will understand where your code is failing.
Now many times when I get to this error, usually means that the directory does not exist but more frequently that you do no have permission to save the file.
Good luck, I hope it helps.
http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html

How to avoid 'headers already sent' within Promise chain?

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.

Categories

Resources