Automatically Manage createdAt and updatedAt Timestamps in Mongoose Schemas
Add automatic createdAt and updatedAt fields to Mongoose schemas using default values and pre‑save middleware, with steps to verify and recover.
27 Sept 2026, 21:17 UTC

Desired outcome
Every document persisted through Mongoose receives a createdAt timestamp on insert and an updatedAt timestamp that refreshes on each save, without requiring manual assignment in application code.
Prerequisites
- Node.js ≥ 14
- Mongoose ≥ 6.0 (the API used here differs from 5.x)
- A defined Mongoose schema for the model you want to timestamp
- Access to a MongoDB instance reachable via a connection string
Focused procedure
- Add Date fields to the schema
const mongoose = require('mongoose'); const { Schema } = mongoose; const articleSchema = new Schema({ title: { type: String, required: true }, content: String, // explicit timestamp fields createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } });The
default: Date.nowensures a value is set when a new document is created. - Attach pre‑save middleware to refresh
updatedAtarticleSchema.pre('save', function (next) { // `this` refers to the document being saved this.updatedAt = Date.now(); // If the document is new, also set createdAt (covers edge‑cases where default may be overridden) if (this.isNew) { this.createdAt = Date.now(); } next(); });Place this block before calling
mongoose.model()so the middleware is compiled into the model. - Compile and use the model
const Article = mongoose.model('Article', articleSchema); // Example usage (async () => { await mongoose.connect('mongodb://localhost:27017/testdb', { useNewUrlParser: true, useUnifiedTopology: true }); const article = new Article({ title: 'First post', content: 'Hello world' }); await article.save(); console.log('Saved:', article.toObject()); // Update the same document article.content = 'Updated content'; await article.save(); console.log('After update:', article.toObject()); })();
Expected checks
- Insert verification – After the first
save(), bothcreatedAtandupdatedAtshould exist and differ by no more than a few milliseconds. - Update verification – On a subsequent save,
createdAtmust remain unchanged whileupdatedAtis newer than its previous value. - Direct MongoDB check – Using the mongo shell or a GUI, query the same document and confirm the timestamps match the values logged by Mongoose. This proves the middleware ran; a raw
db.collection.updateOne()bypassing Mongoose will not modify these fields.
Recovery options
If timestamps appear incorrect:
- Remove the middleware – Delete or comment out the
pre('save')block, recompile the model, and redeploy. - Backfill existing documents – Run a one‑time update to set missing values, e.g.:
Article.updateMany(
{ createdAt: { $exists: false } },
[{ $set: { createdAt: '$updatedAt', updatedAt: '$updatedAt' } }]
);
After fixing the model, re‑save any documents that need correct timestamps (a simple doc.save() will trigger the middleware and refresh updatedAt).
Limitations and practical verification
The middleware only affects operations that go through Mongoose’s save() method. Bulk operations (insertMany, updateMany, replaceOne) or direct database commands do not trigger it, so timestamps will not be auto‑populated there. To verify behavior in your environment:
- Insert a document via Mongoose and log the timestamps.
- Perform an update via Mongoose and confirm only
updatedAtchanges. - Execute the same update with the mongo shell (
db.articles.updateOne(...)) and observe that the timestamps remain unchanged.
If you need timestamps for bulk writes, consider using MongoDB’s $currentDate operator or application‑level logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.