Fix: Cannot overwrite model once compiled in Mongoose

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

The Problem

The “Cannot overwrite model once compiled” error in Mongoose occurs, commonly due to attempting to recompile a model using the same schema with the same name, or requiring a module that defines a model multiple times. This leads to Mongoose throwing an error to prevent multiple instances of the same model from conflicting with each other.

Error Prevention & Solution

To prevent this error, you should ensure that models are compiled just once in your application and then shared wherever needed. Utilizing a mongoose model registry or structuring your app so that each model is defined in its own module, and requiring the module rather than the model directly, can be good practices.

Solving the Issue with Singleton Design Pattern

One of the conventional methods to solve this problem is implementing a Singleton pattern for requiring Mongoose models. Instead of calling mongoose.model() to compile the model each time, you call a function that checks if the model has already been compiled and, if so, returns the existing model. Here is an example of how to implement a Singleton pattern for your Mongoose models:

import mongoose from 'mongoose';
let userModel;
export const getUserModel = () => {
  if (!userModel) {
    const userSchema = new mongoose.Schema({
      username: String,
      password: String
    });
    userModel = mongoose.model('User', userSchema);
  }
  return userModel;
};

Refactoring to Use Module Caching

Node.js naturally supports module caching. If you define a model in one file and then require that file elsewhere, Node.js will cache the exported value after the first time it is loaded. Accordingly, all subsequent requires will return the already compiled model, thus preventing our error. Structure your model in such a way that it takes advantage of this.

// user.model.js
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
  username: String,
  password: String
});
const User = mongoose.model('User', userSchema);
export default User;

// otherfile.js
import User from './user.model';
// Use User model directly

Using Mongoose Connection Instance

Another method is to access models via the connected Mongoose instance. Since model names are unique per connection, attempting to obtain the model from the connection can help circumventing overwriting issues. An example of using the connection object to define or retrieve a model:

import mongoose from 'mongoose';
const connection = mongoose.createConnection(myDbUri);

const User = connection.models.User || connection.model('User', new mongoose.Schema({
  username: String,
  password: String
}));

Note that as per Node.js and JavaScript/TypeScript’s latest syntax, we are making use of ES modules, Promises with async/await, and arrow functions. This ensures our code is modern and consistent with current best practices.