What does "this" refers to in the code below? - node.js

I was looking at some tutorial, there I found a piece of code where I stuck.Please help me to understand this code.I have marked the questions in my comments.
Code
UserSchema.pre('save', function(next){ //this is a pre hook which is used.understood.
var user = this; // what is the function of this?
var SALT_FACTOR = 5;
if(!user.isModified('password')){ //not understood.From where this function arises?I did not found this anywhere in mongoose tutorial/api.
return next();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt){
if(err){
return next(err);
}

A "pre-save" middleware in Mongoose is "document middleware".
The documentation states:
...in document middleware, this refers to the document being updated.
So this refers to the document to be saved.
This also provides a clue as to what isModified is: it's a document method that can be used to check if a particular field, password in this case, has been modified since the document was retrieved from the database earlier.
In the code you're posting, if the password hasn't been changed, there's not need to hash it again (using bcrypt), so that step is skipped by calling next and returning from the middleware.
isModified is documented here: http://mongoosejs.com/docs/api.html#document_Document-isModified

Related

Mongoose schema method returning is not a function

userSchema.method.comparePassword = async function(enteredPassword){
return await bcrypt.compare(enteredPassword, this.password);
}
So in the above code I have a mongoose User schema method in for my users database for comparing the encrypted passwords stored in my database each time a user tries to login.
const ispasswordMatched = await User.comparePassword(password);
In this code is where I call this method in the controler but each time I make a request with postman, it returns an error saying User.comparePassword is not a function. I've searched for a while about others experiencing this but nothing could solve my problem and it left me baffled on how to proceed about solving this. Any advice or tips is greatly apriciated.
Quick edit I am using mongodb version 4.4 with mongoose version 5.12.5
I think this is a typo, you're missing the "s" after method. Try this:
userSchema.methods.comparePassword = async function(enteredPassword){
return await bcrypt.compare(enteredPassword, this.password);
}
Mongoose document here
I know this is already answered but I had the same problem and it fixed by using findOne() instead of find().
i would prefer to use it like the referred method in the mongoose docs
https://mongoosejs.com/docs/api.html#schema_Schema-method
where mentioning the name is an argument for the method function.Not like what you have done here
eg.
const schema = kittySchema = new Schema(..);
schema.method('meow', function () {
console.log('meeeeeoooooooooooow');
})
const Kitty = mongoose.model('Kitty', schema);
const fizz = new Kitty;
fizz.meow();
I faced similar error in my code.. later on I realised that that my custom static schema method returns an array even when there is only a single document... the following code worked.
const user = await User.findByName("Meghna")
//user.sayHi() -> this one was failing with not a function error
user[0].sayHi()
console.log(user)

Passing REST URL parameter as query condition in Mongoose

Newbie in nodejs/mongoDB here. I've tried, with no success,
to find the answer to my problem before posting here.
I'm creating a simple node RESTAPI get services with Mongoose. I'm trying to pass the field value of the collection to retrieve a specific document.
Like this http://localhost:3000/infrakpi/fieldvalue in the browser
I've written this following piece of code.
app.get('/infrakpi/:system',(req, res) => {
Infrakpi.getInfrakpiBySystem(req.params.system, function(err, infrakpibysystem){
if (err) {
throw err;
}
res.json(infrakpibysystem);
});
});
I have defined the get method in my mongoose model like below.
//Get Infrakpi by System
module.exports.getInfrakpiBySystem = function (system, callback) {
Infrakpi.find({system: 'system'}, callback)
}
when system is passed as fieldvalue in the restURL, I want to retrieve the specific document in the collection.
I understand that this may be very basic question but I get result when I use findById for _id field. But client will call only with the specific field.
Appreciate your help.
Not Sure if i can call it stackoverflow luck. I overlooked the quotes in get method in the model. Once I removed them, it worked.
//Get Infrakpi by System
module.exports.getInfrakpiBySystem = function (system, callback) {
Infrakpi.find({system: system}, callback)
}
I'm leaving the question here without deleting, if it can help someone else in the future.

Passport.js - serializeUser doesn't find user properties after lodash _.merge

I have a strange problem with implementing serializeUser() on my express app. My data model has called for a single type of "user" to exist in two models, basically an Identity (username, password, role, etc) and then role-specific data, such as Author (data specific to the Author role). I solve this by having a ref (ObjectID) of an Identity stored inside my Author model. Thus, my signup function appears as follows (simplified):
// Mongoose will just take what it needs for each model
var identity = new Identity(req.body);
var author = new Author(req.body);
identity.save(function(err) {
if (!err) {
// Save reference to identity
author._identity = identity._id;
// Save author document, then login
author.save(function (err) {
// create new "user" object from both identity and author objects
var user = _.merge(identity, author);
req.login(user, function(err) { res.json(user); });
});
}
});
This is working fine and everything is stored nicely in mongo. However, for serializeUser(), I want to store user._identity, not user._id (or user.id as it is strangely used), in order to access the ObjectID of the Identity document, not the Author document:
passport.serializeUser(function(user, done) {
console.log('user', user); // returns full, concatenated object
console.log('_id', user._id); // returns ObjectID value
console.log('_iden', user._identity); // returns undefined
// breaks with a "Failed to serialize user into session" error
done(null, user._identity);
});
What is strange is that console.log(user) gives the full (concatenated object), and user._id returns a value, but user._identity returns undefined (and no other value is returned for that matter. Any insight into why this might be happening or a different approach I should take?
NOTE: This seems to be caused by the _.merge call. I'm using lodash v.2.4.2
Turns out mongoose doesn't like attempting to _.merge two documents - simply using the document.toObject() method resolved this issue:
var user = _.merge(identity.toObject(), author.toObject());
Not bad at all!

nodejs express profile property in request

I got very confused for one usage:
In the route file:
app.param('userId', users.load);
And the users.load function:
exports.load = function (req, res, next, id) {
var options = {
criteria: { _id : id }
};
User.load(options, function (err, user) {
if (err) return next(err);
if (!user) return next(new Error('Failed to load User ' + id));
req.profile = user;
next();
});
};
Here, route should have the userId to response but why does the author use req.profile here. profile is not a property.
Anyone can help?
Thanks.
What the code does is this: for routes that have a userId parameter (that is, routes that look similar to this: /user/:userId), Express will call the load() function before the route handler is called.
The load function loads the user profile belonging to the userId from the database, and adds it to req as a newly created property req.profile.
The .profile property name is arbitrarily named by the author and demonstrates the fact that it's perfectly valid to add properties to req (or res, for that matter, but convention is to add these properties to req).
In the route handler, you can then use req.profile. It's basically a way of propagating data from middleware and app.param() implementations to other parts of the route handling.
the line req.profile = users; think of it this way, 'i want to take all the powers of the users and paste them to req.profile' why? remember this part is sort of a middleware if you want to target any of the read, update and delete code it has to pass through here, it only makes sense if it involves the req, because you are practically requesting to access the said pages (read, edit and delete or any other:userId page) now the profile name doesn't matter you could use any name but its sort of a convention in the community to use the profile name.

Tidy callbacks node.js

Trying to think of a logical way of structuring this. For simplicity, I am creating a user registration page utilising Node.js, Redis and Express.js.
User posts details to page.
Username is confirmed to be valid then Redis checks username is unique. If it is, we continue, if it isn't we return an error message and all the previous details.
Password is confirmed to be valid. If it isn't an error is returned and we don't continue.
Email is confirmed to be unique with Redis. If it is we continue, if it isn't we return an error message and stop.
If no errors at this point, the data is inserted into Redis.
This seems very simple, but using callbacks has generated a total mess - particularly when it comings to returning an error.
How can I structure this in a tidy way?
What you've experienced is callback hell. There are a variety of things you could do like name your callback functions instead of inlining them, follow the same pattern etc...
However, I would recommend you have a look at the async module.
In your, very typical, case I would use async.series like this:
var validateUserName = function(username, callback){
if(username!='valid'){
return callback('invalid username');
}
};
var checkRedis = function(username, callback){
// check to redis
};
var checkPassword = function(password, callback){
// if password valid callback();
// else callback('invalid password');
}
etc...
async.series([checkUserName, checkRedis, checkPassword, etc...], next);

Resources