Why can't I delete a mongoose model's object properties?

When a user registers with my API they are returned a user object. Before returning the object I remove the hashed password and salt properties. I have to use

user.salt = undefined;
user.pass = undefined;

Because when I try

delete user.salt;
delete user.pass;

the object properties still exist and are returned.

Why is that?


Solution 1:

To use delete you would need to convert the model document into a plain JavaScript object by calling toObject so that you can freely manipulate it:

user = user.toObject();
delete user.salt;
delete user.pass;

Solution 2:

Non-configurable properties cannot be re-configured or deleted.

You should use strict mode so you get in-your-face errors instead of silent failures:

(function() {
    "use strict";
     var o = {};
     Object.defineProperty(o, "key", {
         value: "value",
         configurable: false,
         writable: true,
         enumerable: true
     });
     delete o.key;
})()
// TypeError: Cannot delete property 'key' of #<Object>

Solution 3:

Rather than converting to a JavaScript object with toObject(), it might be more ideal to instead choose which properties you want to exclude via the Query.prototype.select() function.

For example, if your User schema looked something like this:

const userSchema = new mongoose.Schema({

    email: {

        type: String,
        required: true,
    },
    name: {

        type: String,
        required: true
    },
    pass: {

        type: String,
        required: true
    },
    salt: {

        type: String,
        required: true
    }
});

module.exports = {

    User: mongoose.model("user", userSchema)
};

Then if you wanted to exclude the pass and salt properties in a response containing an array of all users, you could do so by specifically choosing which properties to ignore by prepending a minus sign before the property name:

users.get("/", async (req, res) => {

    try {

        const result = await User
            .find({})
            .select("-pass -salt");

        return res
            .status(200)
            .send(result);
    }
    catch (error) {

        console.error(error);
    }
});

Alternatively, if you have more properties to exclude than include, you can specifically choose which properties to add instead of which properties to remove:

        const result = await User
            .find({})
            .select("email name");

Solution 4:

Another solution aside from calling toObject is to access the _doc directly from the mongoose object and use ES6 spread operator to remove unwanted properties as such:

user = { ...user._doc, salt: undefined, pass: undefined }