Mongoose Schema vs Mongo Validator
I use both because they each have different limitations:
Mongoose validators do not run on all types of update queries, and validators only run on paths with values in the update doc because the validators can't know if, for example, a required field is already defined in the database but not in your client's memory (see issue). This is a major reason to use MongoDB validators [in addition to Mongoose validators].
update validators only run on
$set
and$unset
operations (and$push
and$addToSet
in >= 4.8.0).So you can have a field with
required: true
in your Mongoose schema, but anupdate
operation will not actually require that field! A MongoDB validator can solve this:db.runCommand({collMod: "collection", validator: {myfield: {$exists: true}}})
MongoDB for the most part cannot reference other fields during validation. For example, you can't say
{field1: {$lte: field2}}
. Mongoose validators can reference other fields.You can do some very basic types of cross-field referencing though:
{validator: {myfield1: "Value 1", $and: [/* other validators */]}
This comes in handy if you're using Mongoose discriminators (inheritance) and have different requirements for each child type.
MongoDB does not provide "nice" errors in case of validation failure; it simply says something like
writeError: {code: 121, errmsg: "Document failed validation}
. Mongoose will typically say something likePath 'foo.bar' failed validation
.MongoDB is fixing this in v4.6.
Abilities that they share:
Type validation. Mongoose by default attempts to cast values to the type specified in the schema. MongoDB with the
$type
attribute will cause a validation failure in case of a type mismatch.Min and max number values. Mongoose uses
min
andmax
attributes on the schema. MongoDB uses$lt
,$lte
,$gt
and$gte
.String enums. Mongoose uses
enum: [values]
. MongoDB uses$in: [values]
.String length validation. Mongoose:
minlength: 2, maxlength: 10
. MongoDB, use a regex:{fieldname: {$regex: /.{2,10}/}}
.Array length validation. Mongoose you have to use a custom validator. MongoDB:
{fieldName: {$size: 2}}
.String RegExp matching. Mongoose you have to use a custom validator.
The first bullet point is a major one. MongoDB does not have transactionsnow has transactions, but it does have powerful (and cheap) atomic updates. You often times can't reliably or safely read -> change -> validate -> write with MongoDB, so using MongoDB native validators is critical in these cases.
Since the last answer, MongoDB 4.0 have been released.
the $jsonSchema
feature now have more options than base mongoose Schema validator. (you can add custom validator in mongoose, though).
the use of allOf
, oneOf
, anyOf
and not
operator permit to do complex matching, similar to Mongoose discriminator.
with the $exec
command, it is possible to compare the value of two field of the same document like so :
db.createCollection("test", {
validator : {
$expr : {$gte: ["$budget", "$spend"]}
}
})
will validate that the value of the field budget
must be greater or equal than the value of spend
.
(example adapted from mongodb documentation)
MongoDB still have the problem of non informative error message. Personnally, I validate my data client side (making request to database if necessary to check for uniqueness). This way, the validation of mongodb have error only if there is concurent modification (someone modified the data between the moment you check and the moment you save). when there is mongodb error, I can simply rerun the client side validation to see what wrong.
I think Mongoose is used to it's fullest when used with a find-modify-save strategy, witch permit to use all the feature. this strategy need the use of versioning or locking to prevent concurrent modification.
when going for atomic update (using mongodb operator, update or findAndModify), with the current state of mongodb validation, I would be tempted to not use mongoose (or only use it for the connection management)