Mongoose: How to define a schema for a collection

Updated: December 30, 2023 By: Guest Contributor Post a comment

Overview

Mongoose is a popular object data modeling (ODM) library for MongoDB in Node.js. It provides a straightforward, schema-based solution to model your application data. In Mongoose, a schema represents the structure of a particular document within a collection. This guide will discuss how to define a schema with Mongoose, explore various data types, and show how to create robust, feature-rich schemas with validations, default values, and custom middleware that suit the needs of your application.

Getting Started

Before we define a schema in Mongoose, you need to have Node.js and MongoDB installed, and you’ll need to install Mongoose in your project:

npm install mongoose

Now let’s start by importing mongoose and establishing a connection to the MongoDB server:

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/my_database', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

Basic Schema Definition

A schema is defined by creating an instance of mongoose.Schema. Let’s create a simple schema for a user collection:

const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  age: Number
});

Here we have defined a very basic schema where each document in the users collection will have a name, email, and age fields, all of which are expected to be strings or a number in the case of age.

Advanced Field Types

Besides the basic String and Number types, Mongoose provides several other types:

  • Date: Represents date and time
  • Buffer: For storing binary data
  • Boolean: For true/false values
  • Mixed: A schemaless equivalent of any type
  • ObjectId: For unique identifiers, generally used for references to other documents
  • Array: An array of items, where items could be any of the other types.
const complexSchema = new mongoose.Schema({
  name: String,
  email: { type: String, unique: true },
  birthDate: Date,
  metaData: Buffer,
  isActive: Boolean,
  tags: [String],
  scores: [Number]
});

Validations

Mongoose also allows you to define some built-in validators in schema:

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  age: {
    type: Number,
    min: 0,
    max: 130
  }
});

Virtuals

Virtuals are document properties that you can get and set but that do not get persisted to MongoDB. The getters and setters of virtuals:

userSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
}).set(function(fullName) {
  const [firstName, lastName] = fullName.split(' ');
  this.firstName = firstName;
  this.lastName = lastName;
});

Middlewares

Middlewares in Mongoose are functions executed after triggering a defined action. Here’s how to define a simple middleware:

userSchema.pre('save', function(next) {
  if (!this.createdAt) {
    this.createdAt = new Date();
  }
  next();
});

Method and Statics

You can also add instance methods and statics to your schemas:

userSchema.methods.sayHello = function() {
  console.log(`Hi, my name is ${this.name}!`);
};

userSchema.statics.findByName = function(name) {
  return this.find({ name: new RegExp(name, 'i') });
};

Plugins

Plugins are a means to re-use schema logic and Bootstrap standard behavior. Here’s how to apply a plugin:

const findOrCreate = require('mongoose-findorcreate');

userSchema.plugin(findOrCreate);

Compiling Model

Once a schema is defined, you compile it to a model which you use to interact with the corresponding collection in your database:

const User = mongoose.model('User', userSchema);

Conclusion

Defining a schema in Mongoose is an important first step in mapping out how you want data to be structured and stored within your MongoDB database. With various types, built-in validation rules, custom methods, and numerous configuration options, Mongoose schemas provide a powerful API to reliably manage your data. With practice and by applying the principles described in this guide, you’ll be able to create efficient and robust data models suited to your application’s needs.