An Introduction to Mongoose's `save()` Function
Mongoose's save()
function
is one way to save the changes you made to a document to the database. There are several
ways to update a document in Mongoose, but save()
is the
most fully featured. You should use save()
to update a document unless you have a good reason not to.
Working with save()
save()
is a method on a Mongoose document.
The save()
method is asynchronous, so it returns a promise that you can
await
on.
When you create an instance of a Mongoose model using new
, calling save()
makes Mongoose insert a new document.
const Person = mongoose.model('Person', Schema({
name: String,
rank: String
}));
const doc = new Person({
name: 'Will Riker',
rank: 'Commander'
});
// Inserts a new document with `name = 'Will Riker'` and
// `rank = 'Commander'`
await doc.save();
const person = await Person.findOne();
person.name; // 'Will Riker'
If you load an existing document from the database and modify it, save()
updates the existing document instead.
const person = await Person.findOne();
person.name; // 'Will Riker'
// Mongoose _tracks changes_ on documents. Mongoose
// tracks that you set the `rank` property, and persists
// that change to the database.
person.rank = 'Captain';
await person.save();
// Load the document from the database and see the changes
const docs = await Person.find();
docs.length; // 1
docs[0].rank; // 'Captain'
Mongoose's change tracking sends a minimal update to MongoDB based on the changes you made to the document. You can set Mongoose's debug mode to see the operations Mongoose sends to MongoDB.
mongoose.set('debug', true);
person.rank = 'Captain';
// Prints:
// Mongoose: people.updateOne({ _id: ObjectId("...") }, { '$set': { rank: 'Captain' } })
await person.save();
Validation
Mongoose validates modified paths before saving. If you set a field to an
invalid value, Mongoose will throw an error when you try to save()
that document.
const Person = mongoose.model('Person', Schema({
name: String,
age: Number
}));
const doc = await Person.create({ name: 'Will Riker', age: 29 });
// Setting `age` to an invalid value is ok...
doc.age = 'Lollipop';
// But trying to `save()` the document errors out
const err = await doc.save().catch(err => err);
err; // Cast to Number failed for value "Lollipop" at path "age"
// But then `save()` succeeds if you set `age` to a valid value.
doc.age = 30;
await doc.save();
Middleware
Mongoose middleware lets you tell Mongoose to execute a function every time save()
is called. For example, calling pre('save')
tells Mongoose to execute a function before executing save()
.
const schema = Schema({ name: String, age: Number });
schema.pre('save', function() {
// In 'save' middleware, `this` is the document being saved.
console.log('Save', this.name);
});
const Person = mongoose.model('Person', schema);
const doc = new Person({ name: 'Will Riker', age: 29 });
// Prints "Save Will Riker"
await doc.save();
Similarly, post('save')
tells Mongoose to execute a function after calling save()
. For example, you can combine pre('save')
and post('save')
to print out how long save()
took.
const schema = Schema({ name: String, age: Number });
schema.pre('save', function() {
this.$locals.start = Date.now();
});
schema.post('save', function() {
console.log('Saved in', Date.now() - this.$locals.start, 'ms');
});
const Person = mongoose.model('Person', schema);
const doc = new Person({ name: 'Will Riker', age: 29 });
// Prints something like "Saved in 12 ms"
await doc.save();
save()
middleware is recursive, so calling save()
on a parent document also triggers
save()
middleware for subdocuments.
const shipSchema = Schema({ name: String, registry: String });
shipSchema.pre('save', function() {
console.log('Save', this.registry);
});
const schema = Schema({
name: String,
rank: String,
ship: shipSchema
});
const Person = mongoose.model('Person', schema);
const doc = new Person({
name: 'Will Riker',
age: 29,
ship: {
name: 'Enterprise',
registry: 'NCC-1701-D'
}
});
// Prints "Save NCC-1701-D"
await doc.save();
doc.ship.registry = 'NCC-1701-E';
// Prints "Save NCC-1701-E"
await doc.save();