I find it very hard to write reliable validation code when values are silently converted to another type.
#!/usr/bin/env node
(async function () {
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true});
const schema = new mongoose.Schema({
text: String
});
const Model = mongoose.model('model', schema);
const doc = new Model();
doc.text = 123123;
console.log(doc.text);
console.log(typeof doc.text);
console.log(doc.validateSync());
})();
I think there should be a toggle to enable strict mode which would cause a validation error when there is a type mismatch.
This is a great idea. We'll give some thought to the implementation and add this. Thanks @sebastian-nowak !
Thanks @lineus, that's great news :)
I think one edge case to consider is what to do about ObjectIds. I rarely see code that creates them as objects, they're often used as strings. I don't know what's the best option.
I think I would be in favor of the first option, but I don't have a strong opinion. One benefit of this approach is that it's probably the easiest option to implement.
Cheers!
@sebastian-nowak what do you think about making it possible to edit this on a per-schema-type basis? So instead of toggling casting on/off at the global level, you can do stuff like:
mongoose.Number.cast(false); // Disable casting for numbers
mongoose.ObjectId.cast(v => typeof v === 'string' ? new ObjectId(v) : v); // Custom casting for ObjectIds
What do you think of that idea?
I think it would be okay for majority of use cases, assuming that:
A nice extra thing to have would be an ability to override the cast
method on a schema level. It would add a lot of flexibility and definitely cover all use cases:
mongoose.String.cast(false);
const schema = new mongoose.Schema({
userId: {
type: String,
cast: (
v => typeof v === 'number' ?
String(v) :
throw new SomeKindOfValidationError('Validation error goes here')
)
}
});
Yeah that sounds like a great idea as well. Right now, Mongoose's cast functions throw a CastError
if casting fails, so your logic would work in theory. I'll prioritize custom cast functions for built-in types for the next release.
Are there docs for this somewhere?
@ronakvora we could use a tutorial for this, but right now the only docs are the schematype API docs for SchemaType.cast()
@vkarpov15 thanks for this solution, it's actually vital for validation!
From what i understand by reading the documentation the override concerns all the properties of that specific type. I think @sebastian-nowak's suggestion for a more fine-grained solution will be very welcomed in addition to the current solution:
A nice extra thing to have would be an ability to override the cast method on a schema level. It would add a lot of flexibility and definitely cover all use cases:
const schema = new mongoose.Schema({ userId: { type: String, cast: ( v => typeof v === 'number' ? String(v) : throw new SomeKindOfValidationError('Validation error goes here') ) } });
For simplicity, also consider adding something like the following option that can keep it clean for some cases where you just don't want any casting:
const schema = new mongoose.Schema({
userId: {
type: String,
cast: false
}
});
What do you say?
@nironater we added something similar for #8300 for the ability to change the cast error for a given path, I opened a separate issue to track this feature request: #8407
Most helpful comment
@sebastian-nowak what do you think about making it possible to edit this on a per-schema-type basis? So instead of toggling casting on/off at the global level, you can do stuff like:
What do you think of that idea?