Mongoose 5.6.0 was released last week. This new release has 12 new features, 2 performance improvements, and several docs improvements. The most interesting new feature is immutable properties. The idea is that marking a property as immutable means that property cannot change after the document is created.

Motivation

One of the primary goals for Mongoose is allowing you to safely write untrusted data to MongoDB without any additional validation. Given a req.body in Express, Mongoose should allow you to set() the modified values and save the data to the database with minimal extra work.

Mongoose timestamps set a createdAt when the document is created, and an updatedAt when the document is updated. Ideally, createdAt shouldn't change once the document is created. But, currently, you can overwrite createdAt !

const Model = mongoose.model( 'Test' , new Schema({ name: String }, { timestamps: true })); const doc = await Model.create({ name: 'test1' }); console .log(doc.createdAt); doc.set({ createdAt: new Date ( '2019-06-01' ) }); await doc.save(); console .log(doc.createdAt);

This means you need to be careful to strip out createdAt from any request bodies. Otherwise, a bug or a malicious user might set an incorrect createdAt .

Introducing Immutable Properties

In Mongoose 5.6.0, you can make a property immutable. If you mark createdAt as immutable, Mongoose will disallow changing createdAt .

const Model = mongoose.model( 'Test' , new Schema({ name: String , createdAt: { type: Date , immutable: true } }, { timestamps: true })); const doc = await Model.create({ name: 'test1' }); console .log(doc.createdAt); doc.set({ createdAt: new Date ( '2019-06-01' ), name: 'test' }); await doc.save(); console .log(doc.name); console .log(doc.createdAt);

Immutability only applies to documents that have already been saved to the database. In other words, you can modify immutable properties if the document's isNew property is false .

const Model = mongoose.model( 'Test' , new Schema({ name: String , createdAt: { type: Date , immutable: true } }, { timestamps: true })); const doc = new Model({ name: 'test1' }); doc.createdAt = new Date ( '2019-06-01' ); await doc.save(); doc.createdAt;

Mongoose also strips updates to immutable properties from updateOne() , updateMany() , and findOneAndUpdate() . Your update will succeed if you try to overwrite an immutable property, Mongoose will just strip out the immutable property.

const schema = new Schema({ name: String }, { timestamps: true }); schema.path( 'createdAt' ).immutable( true ); const Model = mongoose.model( 'Test' , schema); let doc = await Model.create({ name: 'test1' }); doc = await Model.findOneAndUpdate({}, { name: 'test' , createdAt: new Date ( '2019-06-01' ) }, { new : true }); console .log(doc.name); console .log(doc.createdAt);

Moving On