Alan Duong
Alan Duong

Reputation: 11

OverwriteModelError: Cannot overwrite model once compiled, Next.js, Mongoose, Typescript

This is a very simple, early stage application I am making with Next.js, Mongoose, and Typescript. See below code first, below. Every time I click save and try to send any request with Postman, it doesn't work and I get the error message in the console:

OverwriteModelError: Cannot overwrite Expertise Post model once compiled.

I've combed through every stack overflow post regarding this problem, and have implemented those changes, namely the one telling me to do: export default mongoose.models.ExpertisePost || mongoose.model<ExpertisePostInterface>("Expertise Post", expertisePostSchema);, but I've had no luck.

My Problem: Everytime I do a CRUD action with Postman, I get the error message above. I can try to recompile, but it still does not work. One thing to note is that after a fresh recompile (done via redoing npm run dev to restart the server), I am able to do a single GET request for all of the posts. After that, I am unable to do anything. Not even another GET request.

Also, one thing I am seeing is that everytime that error happens, doing a console.log of mongoose.models.ExpertisePost results in undefined. Which means that everytime a CRUD action is done, mongoose.models.ExpertisePost is undefined so then it is never read, and thus the program tries to Overwrite the model using the other code: mongoose.model("Expertise Post", expertisePostSchema);. This isn't allowed, as you don't want to overwrite a model once the application is already compiled. So what should be happening is that mongoose.models.ExpertisePost should return an already compiled model, but it is undefined.

Here are the files that have to do with this problem:

From expertisePost.ts, a file that represents a mongoose model for an expertisePost.

import mongoose from 'mongoose'


// 1. Create an interface representing a document in MongoDB.
interface ExpertisePostInterface {
    title: string;
    description: string;
    pricePerSubmission: Number;
    ratings: Number;
    numOfReviews: Number;
    images: Array<Object>;
    category: String;
    reviews: Array<Object>;
    user: mongoose.Schema.Types.ObjectId;
    createdAt: Date;
}


// 2. Create a Schema corresponding to the document interface.
const expertisePostSchema = new mongoose.Schema<ExpertisePostInterface>({
    title: {
        type: String,
        required: [true, 'Please enter a title for this post.'],
        trim: true,
        maxLength: [100, 'Room name cannot exceed 100 characters']
    },
    description: {
        type: String,
        required: [true, 'Please enter a description for this post.'],
    },
    pricePerSubmission: {
        type: Number,
        required: [true, 'Please enter a price per bite-sized submission.'],
        default: 0.0
    },
    ratings: {
        type: Number,
        default: 0
    },
    numOfReviews: {
        type: Number,
        default: 0
    },
    images: [
        {
            public_id: {
                type: String,
                required: true
            },
            url: {
                type: String,
                required: true
            }
        }
    ],
    category: {
        type: String,
        required: [true, 'Please enter advice category'],
        enum: {
            values: [
                'Writing',
                'College Application',
                'Leadership',
                'Sports',
                'Music',
                'Career',
                'Other'
            ],
            message: 'Please select a correct advice category.'
        }
    },
    reviews: [
        {
            user: {
                type: mongoose.Schema.Types.ObjectId,
                ref: 'User',
                required: true
            },
            name: {
                type: String,
                required: true
            },
            rating: {
                type: Number,
                required: true
            },
            comment: {
                type: String,
                required: true
            }
        }
    ],
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: false
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

// 3. Create a Model.
export default mongoose.models.ExpertisePost || mongoose.model<ExpertisePostInterface>("Expertise Post", expertisePostSchema);

From expertisePostControllers.ts, a file that imports the above model and uses it in many express-like functions for various CRUD actions.

import type { NextApiRequest, NextApiResponse } from 'next'
import ExpertisePost from '../models/expertisePost'




//Get all expertisePosts => GET /api/expertisePosts
const allExpertisePosts = async (req: NextApiRequest, res: NextApiResponse) => {
    try {
        const expertisePosts = await ExpertisePost.find();
    
        res.status(200).json({
            success: true,
            expertisePosts
        })
    } catch (error) {
        res.status(400).json({
            success: false,
            error: error
        })
    }
}

//Get single expertisePost => GET /api/expertisePosts/:id
const getSingleExpertisePost = async (req: NextApiRequest, res: NextApiResponse) => {

    try {
        const expertisePost = await ExpertisePost.findById(req.query.id);
    
        if (!expertisePost) {
            return res.status(404).json({
                success: false,
                error: 'Expertise Post not found with this ID'
            })
        } 

        res.status(200).json({
            success: true,
            expertisePost
        })
    } catch (error) {
        res.status(400).json({
            success: false,
            error: error
        })
    }
}

//Create new expertisePost => POST /api/expertisePosts
const newExpertisePost = async (req: NextApiRequest, res: NextApiResponse) => {

    try {
        const expertisePost = await ExpertisePost.create(req.body);

        res.status(200).json({
            success: true,
            expertisePost
        })
    } catch (error) {
        // console.log(error);
        res.status(400).json({
            success: false,
            error: error
        })
    }
}

//Update single expertisePost => PUT /api/expertisePosts/:id
const updateSingleExpertisePost = async (req: NextApiRequest, res: NextApiResponse) => {

    try {
        let expertisePost = await ExpertisePost.findById(req.query.id);
    
        if (!expertisePost) {
            return res.status(404).json({
                success: false,
                error: 'Expertise Post not found with this ID'
            })
        } 

        expertisePost = await ExpertisePost.findByIdAndUpdate(req.query.id, req.body, {
            new: true,
            runValidators: true
        });

        res.status(200).json({
            success: true,
            expertisePost
        })
    } catch (error) {
        res.status(400).json({
            success: false,
            error: error
        })
    }
}


//Delete single expertisePost => DELETE /api/expertisePosts/:id
//Note: haven't handled deleting images yet!
const deleteSingleExpertisePost = async (req: NextApiRequest, res: NextApiResponse) => {

    try {
        let expertisePost = await ExpertisePost.findById(req.query.id);
    
        if (!expertisePost) {
            return res.status(404).json({
                success: false,
                error: 'Expertise Post not found with this ID'
            })
        } 

        expertisePost.remove()

        res.status(200).json({
            success: true,
            message: 'Expertise Post was deleted.'
        })
    } catch (error) {
        res.status(400).json({
            success: false,
            error: error
        })
    }
}
export {
    allExpertisePosts,
    getSingleExpertisePost,
    newExpertisePost,
    updateSingleExpertisePost,
    deleteSingleExpertisePost
}

I tried to do CREATE, GET, PUT, etc in Postman after running npm run dev on the Next.js application, expected to be able to freely do them as I want in Postman, but it ended up not working and I got in the console the error OverwriteModelError: Cannot overwrite Expertise Post model once compiled.

Upvotes: 0

Views: 1735

Answers (1)

Alan Duong
Alan Duong

Reputation: 11

Fixed it! This was happening even after all of the other Stack Overflow fixes because my call to mongoose.models.ExpertisePost wasn't doing anything, because there wasn't even a key called ExpertisePost!. I did a console.log of mongoose.models and saw that what was actually in the dictionary was a string key, 'Expertise Post'. So I changed the call from mongoose.models.ExpertisePost to mongoose.models['Expertise Post']

Hope this helps!

Upvotes: 1

Related Questions