Node + Mongoose Virtual Columns

Baking Logic Into Our Data Models Published by Nathan A. Wilcox on July 23, 2020

Time to read: 5 minutes. Level: Intermediate

Creating a virtual properties allows us to have a calculated logic baked right into our data model. This can be extremely useful to reduce the amount of duplicated code spread throughout our applications. To show how useful this can be, consider an application that displays a document library for easy consumption. 

Below is our model definition. We are creating our document model with the following properties: title, contents, author, and published date. 

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var DocumentSchema = new Schema({
  title: String,
  contents: String,
  author: String,
  published: Date

If we wanted to print a friendly string of 'Published By: Stan Smith on x/x/xxx', we would have to do some simple string concatenation. If we wanted to use this friendly string in a lot of places, we would have to do some simple string concatenation in a lot of places. See where I am going with this? Whenever you repeat logic, it makes updating that logic more of chore than it needs to be. This is where virtual properties can save us.

In the same file as the model definition, we can add the following code to create a virtual property that will format our friendly string of author and publication date information and make it available as on object property.

DocumentSchema.virtual('friendly_publication_info').get(function() {
  return `Published by: ${} on ${this.published}`

With this extension to our data model, we can use this property anywhere we have a reference to an instance of this class. We simply access it the same way we would any other object property.

Let's go a little further to really take advantage of our new property. Let's say we only want to display the date portion if it's within 3 years. After all, no one is going to read a document if they know it's old and out dated. We are going to make the following updates to our virtual property.

DocumentSchema.virtual('friendly_publication_info').get(function() {  
  let _info = `Published by: ${}`
  if(daysBetween(new Date(), this.published) < (365*3)) {
    _info = `${_info} on ${this.published}`
  return _info

function daysBetween(d1, d2) {
  var diff = Math.abs(d1.getTime() - d2.getTime());
  return diff / (1000 * 60 * 60 * 24);

We added a function inside our schema file that checks the amount of days between 2 days. (NOTE: Any code that is not explicitly exported using the node module system will be private to this file.) We use this to make sure that our publish date is within 3 years of the current date. If not, then we don't bother printing that portion of the string in our application. Now, everywhere we print this friendly publication string, this logic will automatically be invoked. If we ever change our mind, and want to hide the date when our documents are older than only 1 year, there is only one place we need to update :)