Currently I have two almost identical schemas:
var userSchema = mongoose.Schema({
email: {type: String, unique: true, required: true, validate: emailValidator},
passwordHash: {type: String, required: true},
firstname: {type: String, validate: firstnameValidator},
lastname: {type: String, validate: lastnameValidator},
phone: {type: String, validate: phoneValidator},
});
And
var adminSchema = mongoose.Schema({
email: {type: String, unique: true, required: true, validate: emailValidator},
passwordHash: {type: String, required: true},
firstname: {type: String, validate: firstnameValidator, required: true},
lastname: {type: String, validate: lastnameValidator, required: true},
phone: {type: String, validate: phoneValidator, required: true},
});
Their only difference is in validation: Users do not need a firstname, lastname or phone. Admins however must have these properties defined.
Unfortunately the above code is not very DRY, as they're almost identical. Therefore I am wondering if it is possible to build an adminSchema
based on the userSchema
. E.g.:
var adminSchema = mongoose.Schema(userSchema);
adminSchema.change('firstname', {required: true});
adminSchema.change('lastname', {required: true});
adminSchema.change('phone', {required: true});
Obviously that's just pseudocode. Is something like this possible?
Another very similar question is if it is possible to create a new schema based on another, and add some more properties to it. For example:
var adminSchema = mongoose.Schema(userSchema);
adminSchema.add(adminPower: Number);
All of these answers seem rather needlessly complicated, with extension helper functions or extend methods applied to the schema's or using plugins/discriminators. I've used the following solution instead which is simple, clean and easy to work with. It defines a blueprint for the base schema, and then the actual schema's are built using the blueprint:
foo.blueprint.js
foo.schema.js
bar.schema.js
You can use the blueprint for the original schema as is, and using
Object.assign
you can extend the blueprint in any way you like for other schema's, without modifying the same object.You can create a Schema Factory function that accepts a Schema definition and optional schema options, which then merges the passed in Schema definition and options with the Schema fields and options which you want to share across schemas. Example illustrating this (assuming you want to share or extend a schema that has the fields
email
andis_verified
and thetimestamps
option enabled):The
SchemaFactory
function can then be called with:Now the
UserSchema
andAdminSchema
will contain both theemail
andis_verified
field as well as have thetimestamps
option enabled, along with the schema fields and options you pass along.Mongoose 3.8.1 now has support for Discriminators. A sample, from here: http://mongoosejs.com/docs/api.html#model_Model.discriminator
The simplest way for extending mongoose schema
I just published a mongoose-super npm module. Although I did some testing, it is still in an experimental stage. I'm interested to know if it works well for the applications of my fellow SO users!
The module provides a inherit() convenience function that returns a child Mongoose.js model based on a parent model and a child schema extension. It also augments models with a super() method to call parent model methods. I added this functionality because it is something I missed in other extension/inheritance libraries.
The inherit convenience function simply uses the discriminator method.
Some people have in other places suggested using utils.inherits to extend schemas. Another simple way would be to simply set up an object with settings and create Schemas from it, like so:
It's a bit ugly though, since you're modifying the same object. Also I'd like to be able to extend plugins and methods etc. Thus my preferred method is the following:
Which happens to answer your second question that yes, you can
add()
fields. So to modify some properties of the Schema, a modified version of the above function would solve your problem: