1

I'm trying to update a user id using Mongoose inside a Nextjs 13 route handler. Here's my code to do that:

import { NextResponse } from "next/server";
import { User } from "@/mongo";

async function handler(request: Request) {
    
    const payload = await request.json();
    const user = payload.data.id;

    if (payload.type === "user.created") {
        try {
            console.log(User);
            const createdUser = await User.create({
                userId: user,
            });
            console.log(createdUser);
            return NextResponse.json({ message: `User: ${user} successfully added` });
        } catch (error) {
            return NextResponse.json({ message: `Error: ${error}` }, { status: 500 });
        }
    }
}

export const GET = handler;
export const POST = handler;
export const PUT = handler;

However, when I make an API request to this endpoint I get an error:

dev: - error Error [TypeError]: Cannot read properties of undefined (reading 'CommentSchema')
dev:     at Module.CommentSchema (webpack-internal:///(sc_server)/./mongo/index.ts:3:104)
dev:     at eval (webpack-internal:///(sc_server)/./mongo/Review.ts:38:40)
dev:     at (sc_server)/./mongo/Review.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:82:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at eval (webpack-internal:///(sc_server)/./mongo/index.ts:11:65)
dev:     at (sc_server)/./mongo/index.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:102:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at eval (webpack-internal:///(sc_server)/./app/api/webhooks/user/route.ts:8:64)
dev:     at (sc_server)/./app/api/webhooks/user/route.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:52:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Fwebhooks%2Fuser%2Froute&page=%2Fapi%2Fwebhooks%2Fuser%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Fwebhooks%2Fuser%2Froute.ts&appDir=%2Fhome%2Fethan-krich%2FEthans%20Work%2FeTech%20Stuff%2Fcommerce-karma%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2Fhome%2Fethan-krich%2FEthans%20Work%2FeTech%20Stuff%2Fcommerce-karma&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D!:15:145)
dev:     at (sc_server)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Fwebhooks%2Fuser%2Froute&page=%2Fapi%2Fwebhooks%2Fuser%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Fwebhooks%2Fuser%2Froute.ts&appDir=%2Fhome%2Fethan-krich%2FEthans%20Work%2FeTech%20Stuff%2Fcommerce-karma%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2Fhome%2Fethan-krich%2FEthans%20Work%2FeTech%20Stuff%2Fcommerce-karma&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D! (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:42:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at __webpack_exec__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:682:39)
dev:     at /home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:683:28
dev:     at Object.<anonymous> (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:686:3)
dev:     at Module._compile (node:internal/modules/cjs/loader:1233:14)
dev:     at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
dev:     at Module.load (node:internal/modules/cjs/loader:1091:32)
dev:     at Module._load (node:internal/modules/cjs/loader:938:12)
dev:     at Module.require (node:internal/modules/cjs/loader:1115:19)
dev:     at require (node:internal/modules/helpers:119:18)
dev:     at requirePage (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/node_modules/next/dist/server/require.js:112:75)
dev:     at /home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/node_modules/next/dist/server/load-components.js:80:84
dev:     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
dev:     at async loadComponentsImpl (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/node_modules/next/dist/server/load-components.js:80:26)
dev:     at async DevServer.findPageComponentsImpl (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/node_modules/next/dist/server/next-server.js:772:36) {
dev:   digest: undefined
dev: }

Here's the relevant parts of my Schema:

Comment Schema:

import mongoose from "mongoose";

export const CommentSchema = new mongoose.Schema ({
    text: {
        type: String,
        required: true,
    },
    author: {
        type: mongoose.Types.ObjectId,
        required: true,
        ref: "User",
    },
    createdAt: {
        type: Date,
        required: true,
        default: () => Date.now(),
    },
    updatedAt: Date,
})

CommentSchema.pre("save", function (next) {
    this.updatedAt = () => Date.now() as Date;
    next();
});

User Schema:

import mongoose from "mongoose";

const UserSchema = new mongoose.Schema({
    userId: {
        type: String,
        required: true,
    },
    bio: String,
    createdAt: {
        type: Date,
        required: true,
        default: () => Date.now(),
    },
    updatedAt: Date,
});

UserSchema.pre ("save", function (next) {
    this.updatedAt = () => Date.now() as Date
    next()
})

export default mongoose.model("User", UserSchema);

I'm using Nextjs 13 with the new app router and Mongoose to connected to MongoDB.

Edit: I fixed this issue on CommentSchema but now I'm getting a new error. It also seems fixing this error brings back the other one. What is happening?:

dev: - error Error [OverwriteModelError]: Cannot overwrite `User` model once compiled.
dev:     at Mongoose.model (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/node_modules/mongoose/lib/index.js:563:13)
dev:     at eval (webpack-internal:///(sc_server)/./mongo/User.ts:25:114)
dev:     at (sc_server)/./mongo/User.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:92:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at eval (webpack-internal:///(sc_server)/./mongo/index.ts:10:63)
dev:     at (sc_server)/./mongo/index.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:102:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
dev:     at eval (webpack-internal:///(sc_server)/./app/api/webhooks/user/route.ts:8:64)
dev:     at (sc_server)/./app/api/webhooks/user/route.ts (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/app/api/webhooks/user/route.js:52:1)
dev:     at __webpack_require__ (/home/ethan-krich/Ethans Work/eTech Stuff/commerce-karma/.next/server/webpack-runtime.js:33:42)
Ethan
  • 118
  • 1
  • 13
  • can you change the `export default` in `UserSchema` to `export default mongoose.models.User || mongoose.model("User", UserSchema);` – Yilmaz Jul 28 '23 at 22:35
  • I'm still getting the same error: Error [TypeError]: Cannot read properties of undefined (reading 'CommentSchema') – Ethan Jul 29 '23 at 02:06
  • do the same thing on commentModel page – Yilmaz Jul 29 '23 at 02:08
  • Yes but I'm using nested Schemas so that won't work with CommentSchema. Is there any other way to fix this issue – Ethan Jul 29 '23 at 02:42
  • if you create a repo, that would help other developers to test the code. – Yilmaz Jul 29 '23 at 02:58
  • Yeah, but this is a closed-source project. – Ethan Jul 29 '23 at 03:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254706/discussion-between-ethan-and-yilmaz). – Ethan Jul 29 '23 at 03:17
  • I did not mean to share your code. create a https://stackoverflow.com/help/minimal-reproducible-example – Yilmaz Jul 29 '23 at 03:37

1 Answers1

0

As documented on Mongoose / Models

When you call mongoose.model() on a schema, Mongoose compiles a model for you.

The article "How to create and export mongoose models correctly inside Next.js" from Niraj Dhungana explains:

Inside Next JS whenever you try to call the model inside another file to read or add the records. Next JS will try to create a new model every time using the mongoose.model method.

But we don't want to create a model every time we only want to create it for the first time when we don't have that model ready. After that we want to use the model which we have already created.

So you need to check first if there is a model called User inside mongoose.models. Use that one or, if not defined, then create a new one with mongoose.model() method.

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

The ?. operator is optional chaining in JavaScript. It allows reading the value of a property located deep within a chain of connected objects without having to validate that each reference in the chain is valid. If any reference is undefined or null, the expression short-circuits, returning undefined rather than throwing an error. In this case, it prevents an error if mongoose.models is undefined.

In your case:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
    name: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true
    },
    admin: {
        type: Boolean,
        default: false
    }
});

module.exports = mongoose.models?.User || mongoose.model('User', userSchema);

I'm getting an error for comment schema: Error [TypeError]: Cannot read properties of undefined (reading 'CommentSchema'). I can't use your solution to fix that since I only want the schema, not the model, to be exported.

That does not seem to be related to model overwriting, but instead seems to be a problem with accessing a schema that has not been defined or exported correctly.

From what I understand, in Mongoose, a schema is not compiled, only models are.
Schemas are used to define the structure of the documents within a model, while models provide an interface for interacting with your database using that schema.
Therefore, it is not necessary to check if a schema has already been defined or not, as schemas do not get stored in the way that models do.

If you are getting an error saying Cannot read properties of undefined (reading 'CommentSchema'), it suggests that the CommentSchema is not correctly defined or exported from its module. That error would typically be seen if you were trying to access the CommentSchema but it has not been defined or not correctly exported from the module where it is defined.

For example:

// commentSchema.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const commentSchema = new Schema({
  text: String,
  author: String,
  // other fields
});

module.exports = commentSchema;

And in another file, you can import it like:

// someOtherFile.js
const commentSchema = require('./commentSchema');

Make sure you are correctly exporting the schema from the module where it is defined, and also importing it in the module where you are trying to use it. If the schema is defined in a different file or module, make sure that you are requiring it correctly in your model file.


Can I use import and export syntax instead of require?

Because using export default CommentSchema throws an error error Error [TypeError]: Cannot read properties of undefined (reading 'default').

Yes, but the syntax will slightly change with modern JavaScript (ES6 and later).

// commentSchema.js
import mongoose from 'mongoose';

const commentSchema = new mongoose.Schema({
  text: String,
  author: String,
  // other fields
});

export default commentSchema;

And in another file, you can import it like:

// someOtherFile.js
import commentSchema from './commentSchema';

Check your version of Node.js to see if it does support ES6 modules (import/export) by default. As of Node.js 12, you can use ES6 modules by using the .mjs extension or by setting "type": "module" in your package.json file. If you are using a bundler like Webpack or a compiler like Babel, it should support ES6 modules regardless.
See also "Specifying ES6 modules in Node 12" and "Node.js - SyntaxError: Unexpected token import"

If you are trying to access a default export that is not defined, make sure your paths are correct and the file you are trying to import does indeed have a default export.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I'm getting an error for comment schema: `Error [TypeError]: Cannot read properties of undefined (reading 'CommentSchema')`. I can't use your solution to fix that since I only want the schema, not the model to be exported. – Ethan Jul 29 '23 at 16:22
  • @Ethan My first answer was only about the "Cannot overwrite `User` model once compiled." error. I have edited the answer to address your comment. – VonC Jul 29 '23 at 16:46
  • Can I use ```import``` and ```export``` syntax instead of ```require```? – Ethan Jul 29 '23 at 17:04
  • Because using ```export default CommentSchema``` throws an error ``` error Error [TypeError]: Cannot read properties of undefined (reading 'default')``` – Ethan Jul 29 '23 at 17:15
  • @Ethan I have edited the answer to address your comment. – VonC Jul 29 '23 at 17:27
  • 1
    I'm importing and exporting in the same file directory so that should not be an issue. – Ethan Jul 29 '23 at 18:01