0

I am building a Node, Express-based API for user authentication. I am using mongo to store the data. Using postman, I submitted a /post request by passing in the following object

{
"username": "abc",
"password": "abc123",
"email": "abc@ghi.com"
}

under req.body.

This is how my /post function looks in the code:

//create one user
router.post('/createUser', async (req, res) => {
  if (!req.body.email || !req.body.password || !req.body.username) {
    return res.status(400).send({
      message: "No username/password/email specified"
    });
  }
  const newUser = new User({
    email: req.body.email,
    username: req.body.username,
    password: req.body.password
  });

await User.create(newUser, (err, data) => {
    //res.send(data);
    if(err) {
      console.log(err);
      res.status(500).send('Error creating user');
    }
  });
});

User.create() method calls .save() method under the covers. I have a pre-condition on saving to encrypt passwords. On running the post, I get an error that says UnhandledPromiseRejectionWarning: Error: data and salt arguments required

I did some console logging and noticed that this is happening because user.password is coming in as undefined. So it looks like my request is not going through properly from the postman.

Edit: Here is the schema:

   const userSchema = new mongoose.Schema({
  id: {
    type: Number
  },
  email: {
    type: String,
    unique: true,
    required: true,
  },
  username: {
    type: String,
    unique: true,
    required: true
  },
  password: {
    type: String,
    required: true
  },
});

userSchema.pre('save', (next) => {
  const user = this;
  console.log(user.password);
  bcrypt.hash(user.password, 10, (err, hash) => {
    if (err) {
      next(err);
    } else {
      user.password = hash;
      next();
    }
  });
});

Can someone please help me understand what's wrong?

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
LearningAsIGo
  • 1,240
  • 2
  • 11
  • 23

3 Answers3

1

You cannot use arrow function in .pre hooks because arrow function does not bind "this". "this" is supposed to refer to each individual user that about to be saved. however if you use "this" inside the arrow function, it will point to the global object. run this code console.log(this) you will see. use arrow functions for standalone functions. in your case, you are creating a method that part of the object so you should avoid using arrow function

I do not use .pre, because some mongoose queries bypass mongoose middleware, so u need to do extra work. so instead I hash the password inside the router, so everything related to user will be in the same place. single source of truth

const bcrypt = require("bcrypt");
router.post('/createUser', async (req, res) => {
  if (!req.body.email || !req.body.password || !req.body.username) {
    return res.status(400).send({
      message: "No username/password/email specified"
    });
  }
  const newUser = new User({
    email: req.body.email,
    username: req.body.username,
    password: req.body.password
  });
//we created newUser and now we have to hash the password
  const salt = await bcrypt.genSalt(10);
  newUser.password = await bcrypt.hash(newUser.password, salt);
  await newUser.save();
  res.status(201).send(newUser)
  //201 code success for something is created
});

here is the list of http status codes:

https://httpstatuses.com/

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • that is most certainly what I was looking for. It is working as expected now. However, I am curious about why pre hook wouldn't work when we are essentially doing the same process – LearningAsIGo Jul 08 '19 at 16:40
0

The password from postman is getting received on your NodeJS code.

In your code:

const newUser = new User({
    email: req.body.email,
    username: req.body.username,
    password: req.body.password
  });

when you do this, your expected output from newUser changes.

so when your code reaches here...

  const user = this;
  console.log(user.password);

Instead of logging user.password try logging user itself like...

console.log(user)

and see if "(const user=this)" is giving what you expected.

  • I tried that and its coming back as undefined. I understand it could be missing req.body parameters as I have mentioned in the question. But I am not able to understand why thats happening. – LearningAsIGo Jul 07 '19 at 16:26
0
signUp: (req, res, next) => {
    bcrypt.genSalt(10, (err, salt) => {
        bcrypt.hash(req.body.password, salt, (err, hashedPass) => {
            let insertquery = {
                '_id': new mongoose.Types.ObjectId(),
                'username': req.body.username,
                'email': req.body.password,
                'salt': salt,
                'password': hashedPass
            };
            user.create(insertquery, function (err, item) {
            });
        });
    });
}
Sourav De
  • 99
  • 4