G. Deward G. Deward - 3 months ago 16
Node.js Question

Mongoose optional minlength validator?

How can I have an optional

string
field type with a
minlength
. The docs (linked here) do not go into detail on this? I have been trying:

var schema = mongoose.Schema({
name: { type: String, required: true, maxlength: 255 },
nickname: { type: String, minlength: 4, maxlength: 255 }
});


Every variation I try returns some type of error.

My goal is to evaluate the
minlength
and
maxlength
only if the value has been supplied.

Answer

Short answer: write a mongoose plugin.

Long answer:

You can add extra attributes to the schema that you desire. You generally write a Mongoose Plugin to actually do something with them. An example of this would be the mongoose-hidden plugin which allows you to define some of your fields as hidden during conversion:

var userSchema = new mongoose.Schema({
  name: String,
  password: { type: String, hide: true }
});
userSchema.plugin(require('mongoose-hidden'));

var User = mongoose.model('User', userSchema, 'users');
var user = new User({ name: 'Val', password: 'pwd' });
// Prints `{ "name": "Val" }`. No password!
console.log(JSON.stringify(user.toObject()));

Note the hide: true attribute on the password: field. The plugin overrides the toObject() function and the custom version removes looks for the attribute and removes the field.

Here's the body of the plugin. Line #4 looks checks for the existence of the schemaType.options.hide attribute:

function HidePlugin(schema) {
  var toHide = [];
  schema.eachPath(function(pathname, schemaType) {
    if (schemaType.options && schemaType.options.hide) {
      toHide.push(pathname);
    }    
  });
  schema.options.toObject = schema.options.toObject || {};
  schema.options.toObject.transform = function(doc, ret) {
    // Loop over all fields to hide
    toHide.forEach(function(pathname) {
      // Break the path up by dots to find the actual
      // object to delete
      var sp = pathname.split('.');
      var obj = ret;
      for (var i = 0; i < sp.length - 1; ++i) {
        if (!obj) {
          return;
        }
        obj = obj[sp[i]];
      }
      // Delete the actual field
      delete obj[sp[sp.length - 1]];
    });

    return ret;
  };
}

My point is...

... if you write a mongoose plugin (for example, maybe the "MinLengthPlugin") you could reuse this on all of your schemas without writing any additional code. Within the plugin you could override the functionality with something like:

module.exports = function MinLenghPlugin (schema, options) {

    schema.pre('save', myCustomPreSaveHandler);

    var myCustomPreSaveHandler = function() {
       // your logic here
    }
};
Comments