Geek Logbook

Tech sea log book

How to Simplify a Mongoose Schema in Node.js

When working with Mongoose in Node.js, defining a schema for your models can get repetitive and verbose, especially if you’re specifying data types and validation repeatedly. In this post, we’ll look at how to simplify a Mongoose schema and clean up your code without sacrificing functionality.

Let’s take a typical Mongoose schema example and explore ways to reduce the amount of code needed.

Original Mongoose Schema

Here’s a typical Mongoose schema for a course:

const mongoose = require('mongoose');

const CourseSchema = new mongoose.Schema({
    title: {
        type: String,
        trim: true,
        required: [true, 'Please add a course title']
    },
    description: {
        type: String,
        required: [true, 'Please add a description']
    },
    weeks: {
        type: String,
        required: [true, 'Please add weeks']
    },
    tuition: {
        type: Number,
        required: [true, 'Please add tuition']
    },
    minimumSkill: {
        type: String,
        required: [true, 'Please add a minimum skill'],
        enum: ['beginner', 'intermediate', 'advance']
    },
    scholarshipAvailable: {
        type: Boolean,
        default: false
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
    bootcamp: {
        type: mongoose.Schema.ObjectId,
        ref: 'Bootcamp',
        required: true
    }
});

module.exports = mongoose.model('Course', CourseSchema);

Identifying Redundancy

As you can see, a lot of the fields share common structure:

  • Each field has a type property.
  • Several fields have required validations.
  • Some fields have default values or trimming options.

Let’s reduce the repetitive parts and create a more compact schema.

Refactoring the Schema

To simplify this, we can use object destructuring and avoid repeating the type field for each property:

const mongoose = require('mongoose');
const { Schema } = mongoose;

const requiredString = {
    type: String,
    required: [true, 'Field is required']
};

const CourseSchema = new Schema({
    title: { ...requiredString, trim: true, message: 'Please add a course title' },
    description: { ...requiredString, message: 'Please add a description' },
    weeks: { ...requiredString, message: 'Please add weeks' },
    tuition: { 
        type: Number, 
        required: [true, 'Please add tuition'] 
    },
    minimumSkill: {
        type: String,
        required: [true, 'Please add a minimum skill'],
        enum: ['beginner', 'intermediate', 'advance']
    },
    scholarshipAvailable: {
        type: Boolean,
        default: false
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
    bootcamp: {
        type: Schema.ObjectId,
        ref: 'Bootcamp',
        required: true
    }
});

module.exports = mongoose.model('Course', CourseSchema);

What’s Changed?

  • Reusable Field Definitions: We created requiredString, a reusable object for all string fields with required: true. This reduces the need to repeat the structure for every string field in the schema.
  • Destructuring: We used the spread operator (...) to merge the requiredString structure with additional properties like trim or custom error messages.

Final Thoughts

By reusing common field definitions, you can write more concise, readable Mongoose schemas. This approach not only makes your code cleaner but also easier to maintain.

For larger schemas, this refactoring style can significantly reduce boilerplate, keeping your code DRY (Don’t Repeat Yourself) while maintaining clarity.