Mongoose - sub document validation not working

The problem here is mostly in how you have constructed your "details" entry here. So despite what you think you may have done, the type of entry that is actually here is a plain sub-document or what would generally be referred to as a "hash/map or dict" depending on which term you are most familiar with.

These are not strictly speaking "typed" in any way, so there is no real control over the "keys" you are placing in there. So what you probably want is something that can actually be structured in a strictly typed way, for example:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var phonesSchema = new Schema({
  country_code: { type: String, required: true },
  region_code: { type: String, required: true },
  number: { type: String, required: true }
});

var minderSchema = new Schema({

  phones:[phonesSchema]

});


var Minder = mongoose.model( 'Minder', minderSchema );

var minder = new Minder({
  "phones": [{ "number": "12345", "bill": "45678" }]
});

console.log( JSON.stringify( minder, undefined, 2 ) );


minder.save();

This not only separates the schema definitions ( which is handy and clean ) but now you have a clearly defined "sub-type" as it were, where the validation can be performed on the entries. You can expand on this if needed, but I generally find this the cleaner form.

A final and important point here is realizing where the "validation" actually happens. So from you example you are just creating instances, but this is not where validation happens. The only place this actually occurs is when the object instance is "saved" and persisted to storage. This allows you to "build" the objects, but is not a strict "validator" of the objects in a traditional "class" sense.

So running the snippet above, you get this output:

{ _id: 537d7c71d4d04b65174d0c00,
  phones: [ { number: '12345', _id: 537d7c71d4d04b65174d0c01 } ] }

events.js:72
        throw er; // Unhandled 'error' event
          ^
No listeners detected, throwing. Consider adding an error listener to your connection.
ValidationError: Path `region_code` is required., Path `country_code` is required.
    at model.Document.invalidate (/home/neillunn/node_modules/mongoose/lib/document.js:1009:32)
    at EmbeddedDocument.invalidate (/home/neillunn/node_modules/mongoose/lib/types/embedded.js:178:19)
    at /home/neillunn/node_modules/mongoose/lib/document.js:958:16
    at validate (/home/neillunn/node_modules/mongoose/lib/schematype.js:610:7)
    at /home/neillunn/node_modules/mongoose/lib/schematype.js:627:9
    at Array.forEach (native)
    at SchemaString.SchemaType.doValidate     (/home/neillunn/node_modules/mongoose/lib/schematype.js:614:19)
    at /home/neillunn/node_modules/mongoose/lib/document.js:956:9
    at process._tickCallback (node.js:419:13)
    at Function.Module.runMain (module.js:499:11)

Noting that the "log" output there kept the "valid" entry but discarded the field that was not defined, and then the "validation" really only occurs for the required fields only when the object actually attempts to "save".

So consider your structure and also what is actually happening with validation in place. Trying to add a field that is not defined does not error, it just discards. Omitting a "required" field, is only checked when the object is persisted which allows you time to build it. These are not required "class constructor" type arguments, but for a different purpose.


If you really want nesting, drop the "type" declaration as in:

var phonesSchema = new Schema({
  details: {
    country_code: { type: String, required: true },
    region_code: { type: String, required: true },
    number: { type: String, required: true }
  }
});

And the validation will work for you:

{
  "_id": "537d9e6d5b433f8745547f52",
  "phones": [
    {
      "_id": "537d9e6d5b433f8745547f53",
      "details": {
        "number": "12345"
      }
    }
  ]
}

events.js:72
    throw er; // Unhandled 'error' event
          ^
No listeners detected, throwing. Consider adding an error listener to your connection.
ValidationError: Path `details.region_code` is required., Path `details.country_code` is required.