Why can't I access a mongoose schema's method?
Asked Answered
S

1

5

I have this Mongoose schema in a Nodejs application:

const mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    sodium = require('sodium').api;

const UserSchema = new Schema({
    username: {
        type: String,
        required: true,
        index: { unique: true }
    },
    salt: {
        type: String,
        required: false
    },
    password: {
        type: String,
        required: true
    }
});

UserSchema.methods.comparePassword = function(candidatePassword, targetUser) {
    let saltedCandidate = candidatePassword + targetUser.salt;
    if (sodium.crypto_pwhash_str_verify(saltedCandidate, targetUser.password)) {
        return true;
    };
    return false;
};

module.exports = mongoose.model('User', UserSchema);

And I created this routes file.

const _ = require('lodash');
const User = require('../models/user.js'); // yes, this is the correct location

module.exports = function(app) {
    app.post('/user/isvalid', function(req, res) {
        User.find({ username: req.body.username }, function(err, user) {
            if (err) {
                res.json({ info: 'that user name or password is invalid. Maybe both.' });
            };
            if (user) {
                if (User.comparePassword(req.body.password, user)) {
                    // user login
                    res.json({ info: 'login successful' });
                };
                // login fail
                res.json({ info: 'that user name or password is invalid Maybe both.' });
            } else {
                res.json({ info: 'that user name or password is invalid. Maybe both.' });
            };
        });
    });
};

I then use Postman to make a call to 127.0.0.1:3001/user/isvalid with an appropriate Body content. The terminal says tell me TypeError: User.comparePassword is not a function and crashes the app.

Since the if (user) bit passes, that indicates to me that I have properly retrieved a document from Mongo and have an instance of the User schema. Why is the method not valid?

eta: the module export I failed to copy/paste originally

Shiau answered 17/2, 2017 at 16:25 Comment(2)
Add to end of user model module: module.exports = mongoose.model('User', UserSchema);Laudable
@Laudable i failed to copy/paste that, but it is in my actual code. Good catch. Editing to add itShiau
N
11

This creates instance method:

UserSchema.methods.comparePassword = function(candidatePassword, targetUser) {
    // ...
};

If you want a static method use this:

UserSchema.statics.comparePassword = function(candidatePassword, targetUser) {
    // ...
};

Static methods are when you want to call it as User.comparePassword().

Instance methods are when you want to call it as someUser.comparePassword() (which in this case would make a lot of sense so that you wouldn't have to pass the user instance explicitly).

See the documentation:

Needlepoint answered 17/2, 2017 at 17:25 Comment(1)
so given the code I presented, passing in (user) should mean that user.comparePassword() should work, but it gives the same error. leaving it as is and using UserSchema.statics.comparePassword in the schema definition does work, so I'm not doubting it. I'm just confused, I guess.Shiau

© 2022 - 2024 — McMap. All rights reserved.