Mongoose 8 was launched on October 31, 2023.
This main launch is comparatively small in comparison with Mongoose 6, with solely 14 backwards breaking adjustments.
The driving power for a brand new main launch simply 7 months after Mongoose 7 is the 6.0 launch of the MongoDB Node.js driver: we ship a brand new main model of Mongoose at any time when the MongoDB Node.js driver ships a brand new main model.
We additionally took the chance to take away some deprecated capabilities from Mongoose 7: most notably, Mongoose 8 not helps depend()
or findOneAndRemove()
.
On this weblog publish, I will spotlight another noteworthy adjustments in Mongoose 8.
Mongoose’s model assist coverage hasn’t modified considerably with Mongoose 8.
We’ll nonetheless proceed to ship bug fixes and options to Mongoose 7, in addition to Mongoose 8.
Mongoose 5 and Mongoose 6 are nonetheless supported, however we are going to not backport any bug fixes or options to Mongoose 5 or 6 until we’re particularly requested to.
Mongoose 5 is scheduled for end-of-life on March 1, 2024; we are going to not ship any fixes to Mongoose 5 after that date.
With out additional ado, here is a number of the highlights from Mongoose 8.
Yow will discover the complete migration information on the Mongoose docs.
Decrease on Replace
Mongoose’s decrease
choice tells Mongoose to recursively take away empty objects when saving to MongoDB.
For instance, suppose you’ve a Check
mannequin as follows:
const schema = new Schema({
nested: {
field1: Quantity
}
});
const Check = mongoose.mannequin('Check', schema);
const { _id } = await Check.create({ nested: {} });
Should you load the uncooked doc from MongoDB utilizing lean()
, you will see that nested
is undefined
, not an empty object.
let rawDoc = await Check.findById(_id).lean();
rawDoc.nested;
Nonetheless, in Mongoose 7, should you set a nested property to an empty object on an current doc, Mongoose will not decrease out that nested property.
For the sake of constructing the API constant, we made Mongoose 8 additionally decrease out empty nested properties when saving an current doc.
const doc = await Check.findById(_id);
doc.nested = {};
doc.markModified('nested');
await doc.save();
rawDoc = await Check.findById(_id).lean();
rawDoc.nested;
If this transformation is problematic for you, you may both disable decrease
in your schema:
const schema = new Schema({
nested: {
field1: Quantity
}
}, {
decrease: false
});
Base Schema Validators Run Earlier than Discriminator Schema Validators
In Mongoose 7, discriminator schemas had been added to the bottom schema; meaning base schema keys got here after discriminator schema keys in key order, so all validators on base schema keys ran earlier than validators on discriminator schema keys.
const schema = new Schema({
identify: {
kind: String,
validate(v) {
console.log('Base schema validator');
return true;
}
}
});
const Check = mongoose.mannequin('Check', schema);
const D = Check.discriminator('D', new Schema({
otherProp: {
kind: String,
get(v) {
console.log('Discriminator schema validator');
return true;
}
}
}));
Mongoose 8 flips the order that identify
and otherProp
are outlined within the D
mannequin’s remaining merged schema.
In Mongoose 8, identify
will present up earlier than otherProp
in situations of D
, and “Base schema validator” will print earlier than “Discriminator schema validator”.
const doc = new D({ identify: 'foo', otherProp: 'bar' });
console.log(doc);
await doc.validate();
Placing discriminator properties after base schema properties is extra intuitive, and helps in instances the place discriminator schema setters depend on base schema setters.
String Enums Permit null
As a normal rule of thumb, Mongoose treats null
and undefined
(so-called nullish values) as interchangeable.
Earlier than Mongoose 8, there was one massive exception to this rule: null
wasn’t allowed as a worth for string paths with an enum
, even when the trail wasn’t required
.
const schema = new Schema({
standing: {
kind: String,
enum: ['active', 'disabled']
}
});
const Check = mongoose.mannequin('Check', schema);
const doc = new Check({ standing: null });
await doc.validate();
This habits was inconsistent for a few causes.
First, whereas null
wasn’t allowed, undefined
was.
const doc = new Check({ standing: undefined });
await doc.validate();
Second, quantity enums allowed null
, string enums did not.
const schema = new Schema({
myNum: {
kind: Quantity,
enum: [1, 2, 3]
}
});
const Check = mongoose.mannequin('Check', schema);
const doc = new Check({ myNum: null });
await doc.validate();
In Mongoose 8, null
is legitimate for string enums, until the trail can be required.
const schema = new Schema({
standing: {
kind: String,
enum: ['active', 'disabled'],
required: true
}
});
const Check = mongoose.mannequin('Check', schema);
const doc = new Check({ standing: null });
await doc.validate();
Transferring On
Mongoose 8 is a comparatively small launch, however we predict the adjustments make Mongoose’s API extra constant and streamlined.
Take a look at the migration information for a full record of all the possibly breaking adjustments.
Please improve and open any points you discover on Mongoose’s GitHub web page!