got `{}` when set a field in mongodb to `undefined` using mongoose - node.js

I'm facing a weird problem
I'm using mongoose 5.12.8, I try to delete a user's image with this route
router.delete('/users/me/avatar', auth, async(req, res) => {
try {
req.user.avatarObj = undefined
await req.user.save()
res.send({ message: "Deleted user's avatar" })
} catch (error) {
res.status(500).send(error.message)
}
});
but when I use get route, i got req.user.avatarObj is still an empty object {}
//get user avatar
router.get('/users/:id/avatar', async(req, res) => {
try {
const user = await User.findById(req.params.id);
console.log(user.avatarObj) // print {} ?????????
console.log(user) //user has no avatarObj field
if (user && user.avatarObj) {
res.set('Content-Type', user.avatarObj.contentType);
res.send(user.avatarObj.data);
} else {
throw new Error('Not found user or image');
}
} catch (err) {
res.status(404).send({ error: err.message });
}
});
this is my userSchema
avatarObj: {
data: Buffer,
contentType: String
}
and some middleware
//hash plain text password before saving
userSchema.pre('save', async function(next) {
const user = this
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 10)
}
next()
});
//remove all tasks before remove user
userSchema.pre('remove', async function(next) {
await Task.deleteMany({ owner: this._id })
next()
});
My question is: Why user.avatarObj is still an empty object?
Thank you guys!

Related

error 'user.findOneAndUpdate is not a function' but data updated

i'm trying to use findOneAndUpdate for change password on mongoose, it's work, database updated
but error message show
"error": "user.findOneAndUpdate is not a function"
this my routes
router.post('/changepass', async (req, res) => {
const { phone, password } = req.body;
try {
const user = await User.findOneAndUpdate({phone},{password})
await user.findOneAndUpdate();
res.send({ user });
} catch (err) {
console.log(err)
res.status(422).send({ error: err.message });
}
});
and this my userSchema
userSchema.pre('findOneAndUpdate', function(next) {
const update = this.getUpdate()
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(update.password, salt, (err, hash) => {
this.getUpdate().password = hash;
return next();
})
})
});
please help me, i'm stuck for hours on this
try removing this line await user.findOneAndUpdate(); This line is causing the error and as per official docs it should be not there
router.post('/changepass', async (req, res) => {
const { phone, password } = req.body;
try {
const user = await User.findOneAndUpdate({phone},{password})
res.send({ user });
} catch (err) {
console.log(err)
res.status(422).send({ error: err.message });
}
});
here is doc reference - https://mongoosejs.com/docs/tutorials/findoneandupdate.html

deleting key from object but still showing in response

I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}

User.destroy is not a function

I use the oracle-sage library. And when I want to delete a record, an error occurs.
{
"success": false,
"message": "User.destroy is not a function"
}
How can fix this?
const User = require('../models/User')
const errorHandler = require('../utils/errorHandler')
module.exports.remove = async function (req, res) {
try {
await User.destroy({
USER_ID: req.params.USER_ID
})
res.status(200).json({
message: 'User deleted.'
})
} catch (e) {
errorHandler(res, e)
}
}
Probably .destroy is not a static method, needs an instantiated object. You can try to get the user object first then destroy it.
try {
let user = await User.findOne({ USER_ID: req.params.USER_ID});
user.destroy().then(function(){
res.status(200).json({
message: 'User deleted.'
})
});
} catch (e) {
errorHandler(res, e)
}
Another way to implement destroy method is as below
const deleteUser = async (req,res) => {
console.log('Deleting User by Id')
const userId = await req.params.id
const user = await User.destroy({
where:{
id : userId
},
raw:true
}).catch(error=>console.log(error))

Correct way to figure out what rejection a promise had?

I have an API / express router:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(500).json("DB Error");
}
});
Currently, on error, it returns 500 DB error. This is my controller:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(e);
if (doc) {
reject("Username already exists.");
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(e);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
What's the right way to return a 400 if username already exists, and a 500 if it was a database error?
Mongoose already uses promises, the use of new Promise is promise construction antipattern.
Express doesn't have the concept of controllers, there are only route handlers and middlewares. Since register should be very aware of the way it will be used in a response, there may be no need for another level of abstraction above route handler. There will be no problem when a function has access to handler parameters and can form a response in-place.
It can be:
router.post("/signup", async function (req, res) {
try {
const { body, password } = req.body;
const user = await User.findOne({ username: username }).lean();
if (user) {
res.status(400).json("Username already exists");
} else {
...
res.json(user);
}
} catch (e) {
res.status(500).json("DB Error");
}
});
In case route handler needs to be reused in multiple places with some variations, it could be refactored to higher-order function or some other helper that is aware of original req and res parameters.
You can change the way you are rejecting the Promise. I'd suggest something like:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(500);
if (doc) {
reject(400);
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(500);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
And in the route:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(e).json(e == 400 ? "Username already exists." : "DB Error");
}
});

how to resolve data and hash error in node js bcrypt

Error: data and hash arguments required
i am doing simple, login signup and forgot password in node js using
bcrypt hash
code : for login
app.post('/login', (req, res) => {
console.log('login');
let {email, password} = req.body;
User.updateOne({email: email}, ' email password', (err, userData) => {
if (!err) {
let passwordCheck = bcrypt.compareSync(password, userData.password);
if (passwordCheck) {
console.log('login2');
req.session.user = {
email: userData.email,
id: userData._id
};
req.session.user.expires = new Date(Date.now() + 3 * 24 * 3600 * 1000);
res.status(200).send('You are logged in, Welcome!');
} else {
res.status(401).send('incorrect password');
console.log('login3');
}
} else {
res.status(401).send('invalid login credentials');
console.log('login4');
}
});
});
code for signUp :
app.post('/signup', (req, res) => {
let {email, password} = req.body;
let userData = {password: bcrypt.hashSync(password, 5, null), email };
console.log('out save');
let newUser = new User(userData);
newUser.save().then(error => {
if (!error) {
console.log('in save');
return res.status(200).json('signup successful');
} else {
if (error.code === 11000) {
return res.status(409).send('user already exist!');
} else {
console.log(JSON.stringigy(error, null, 2));
return res.status(500).send('error signing up user');
}
}
});
});
i have tried console logging few lines and turned out that the code doesn't go into signup
newUser.save();
tell me where i'm going wrong
The issue is with this line newUser.save().then(error => {. Do you notice the .then(). That is a resolved promise so it wouldn't be returning an error. Typically you would see something like this.
Promise()
.then((result) => {
// result is a resolved promise
})
.catch((error) => {
// error is a rejected promise
})
So you should try changing your code to this:
newUser.save()
.then(result => {
console.log('in save')
return res.status(200).json('signup successful')
})
.catch(error => {
if (error.code === 11000) {
return res.status(409).send('user already exist!')
} else {
console.log(JSON.stringigy(error, null, 2))
return res.status(500).send('error signing up user')
}
})
It looks like you're using mongoose, here is the API docs for Document.prototype.save() https://mongoosejs.com/docs/api.html#document_Document-save
Their documentation uses callback functions for the most part but if you scroll to the end of the .save() documentation you will see they show one example with a promise.
bcrypt.compareSync takes 2 parameters; passwordToCheck, passwordHash
You are getting error "bcrypt Error: data and hash arguments required"
This error means one or both parameters are either null or undefined,
In your case you need to make sure that password, userData.password are correctly going in function bcrypt.compareSync

Resources