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
typeproperty. - Several fields have
requiredvalidations. - Some fields have
defaultvalues 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 withrequired: true. This reduces the need to repeat the structure for every string field in the schema. - Destructuring: We used the spread operator (
...) to merge therequiredStringstructure with additional properties liketrimor 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.