Getting Started with Mongoose Virtuals

May 11, 2019

Mongoose virtuals are computed properties on Mongoose documents. They are not stored in MongoDB: a virtual property is computed whenever you access it.

Suppose you have a BlogPost model that stores the raw markdown content of a blog post. You can create a virtual html that automatically calls a markdown parser for you whenever you access the html property.

// Markdown parser
const marked = require('marked');

const blogPostSchema = new Schema({ content: String });

// A _virtual_ is a schema property that is **not** stored in MongoDB.
// It is instead calculated from other properties in the document.
blogPostSchema.virtual('html').get(function() {
  // In the getter function, `this` is the document. Don't use arrow
  // functions for virtual getters!
  return marked(this.content);
});
const BlogPost = mongoose.model('BlogPost', blogPostSchema);

const doc = new BlogPost({ content: '# Hello' });
doc.html; // "<h1 id="hello">Hello</h1>"

Why would you use a virtual instead of a method? Because you can configure Mongoose to include virtuals when converting a Mongoose document to JSON, including when using Express' res.json() function.

const app = require('express')();
const axios = require('axios');

// Make Mongoose attach virtuals whenever calling `JSON.stringify()`,
// including using `res.json()`
mongoose.set('toJSON', { virtuals: true });

app.get('*', function(req, res) {
  // Mongoose will automatically attach the `html` virtual
  res.json(doc);
});

const server = await app.listen(3000);

// "<h1 id="hello">Hello</h1>"
await axios.get('http://localhost:3000').then(res => res.data.html);

The downside of virtuals is that, since they aren't stored in MongoDB, you can't use them in queries.


More Mongoose Tutorials