Sling Academy
Home/Node.js/Mongoose: How to define a schema for a collection

Mongoose: How to define a schema for a collection

Last updated: December 30, 2023

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.

Next Article: Mongoose: Defining a schema with nested objects

Previous Article: How to set up and use Mongoose with TypeScript

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates