'Next.JS and Mongoose models

I'm building a pretty simple next.js app for a client and for my life I cannot figure out why I keep getting Cannot overwrite User model once compiled. It seems to work fine everytime I shutdown the development server for the first request to any route: addUser, authenticate, GET... it all works on the first request only. This is my connect function to MongoDB.

const connectDB = (handler) => async (req, res) => {
  if (mongoose.connections[0].readyState) {
    return handler(req, res);
  }
  await mongoose
    .connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useCreateIndex: true,
      useFindAndModify: false,
      useUnifiedTopology: true,
    })
    .then(() => {
      return handler(req, res);
    });
};

const middleware = nc();
middleware.use(connectDB);
// middleware.use(authMiddleware);
module.exports = {
  connectDB,
  middleware,
};

and the model:

const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");

const UserSchema = mongoose.Schema(
  {
    userName: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
);

UserSchema.pre("save", async function (next) {
  if (this.isModified("password")) {
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } else {
    next(new Error("failed to encrypt password"));
  }
});

UserSchema.methods.comparePassword = async function (password, next) {
  const comparison = bcrypt.compare(password, this.password);
  if (!comparison) {
    return {
      isMatch: false,
      comparison: comparison,
    };
  } else {
    return {
      isMatch: true,
      comparison: comparison,
    };
  }
};

module.exports = mongoose.model("User", UserSchema);

and the route:

const handler = nc();
// handler.use(middleware);
handler.post(async (req, res) => {
  console.log(colors.bgBlue("Did run in route..."));
  try {
    const user = await User.findOne({ userName: req.body.email });
    const { isMatch } = await user.comparePassword(req.body.password);
    if (!isMatch) {
      return res.status(401).json({ error: "These passwords do not match." });
    }
    if (isMatch) {
      const payload = {
        user: {
          id: user.id,
        },
      };
      jwt.sign(
        payload,
        process.env.JWT_SECRET,
        {
          expiresIn: 3600,
        },
        (err, token) => {
          if (err) throw err;
          return res.json({ userID: user._id, token });
        }
      );
    }
  } catch (error) {
    console.log(error);
    res.status(500).json({ error: "There was an error adding that user." });
  }
});

export default connectDB(handler);

Sorry for making this so long, I literally have no idea where to even start. I miss express but the SSR is pretty rad :(



Solution 1:[1]

You are overwriting User with user when you call comparePassword.

Change it to User.comparePassword

Explanation: user is your declared variable holding the returned document(s), while User is represanting the model that you are importing \ requiring from mongoose.

Solution 2:[2]

In next.js this is how we should export the mongoose models.

export default mongoose.models.User || mongoose.model("User", userSchema);

That is why instead of common.js you shouldbe using es-modules import export syntax in your models file.

 import mongoose from "mongoose";

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ISAE
Solution 2 Yilmaz