vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.48k stars 27.04k forks source link

Mongodb issues with app directory #45476

Open JahanzaibTayyab opened 1 year ago

JahanzaibTayyab commented 1 year ago

Verify canary release

Provide environment information

Operating System: Platform: darwin Arch: x64 Version: Darwin Kernel Version 21.6.0: Mon Dec 19 20:44:01 PST 2022; root:xnu-8020.240.18~2/RELEASE_X86_64 Binaries: Node: 18.12.1 npm: 8.19.2 Yarn: 1.22.19 pnpm: N/A Relevant packages: next: 13.1.6 eslint-config-next: 13.1.6 react: 18.2.0 react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue

https://github.com/JahanzaibTayyab/portfolio_projects/tree/main/ai-image-generation

To Reproduce

Whenever i request for data then end point throw an error.

Describe the Bug

Mongodb connection

import mongoose from "mongoose";
const main = async () => {
  if (!process.env.MONGODB_URI) {
    throw new Error('Invalid/Missing environment variable: "MONGODB_URI"');
  }
  return await mongoose.connect(process.env.MONGODB_URI as string);
};
export default main;`

Post.tsx

import mongoose from "mongoose";

type post = {
  name: string;
  prompt: string;
  photo: string;
};

const PostSchema = new mongoose.Schema({
  name: { type: String, required: true },
  prompt: { type: String, required: true },
  photo: { type: String, required: true },
});

const Post = mongoose.model("Post", PostSchema);

export default Post;

Post Data API

import type { NextApiRequest, NextApiResponse } from "next";
import main from "@/database/mongodb/mongodb";
import { v2 as cloudinary } from "cloudinary";
import Post from "@/database/mongodb/models/post";
import { validatePost } from "@/database/mongodb/models/post";

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});

export const config = {
  api: {
    bodyParser: {
      sizeLimit: "50mb",
    },
    responseLimit: false,
  },
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    main();
    if (req.method === "GET") {
      try {
        const posts = await Post.find({});
        res.status(200).json({ success: true, data: posts });
      } catch (err) {
        res.status(500).json({
          success: false,
          message: "Fetching posts failed, please try again",
        });
      }
    } else if (req.method === "POST") {
      try {
        const { error } = validatePost(req.body);
        if (error) return res.status(400).send(error.details[0].message);
        const { name, prompt, photo } = req.body;
        const photoUrl = await cloudinary.uploader.upload(photo);
        const post = new Post({
          name,
          prompt,
          photo: photoUrl.url,
        });
        const newPost = await post.save();
        res.status(200).json({ success: true, data: newPost });
      } catch (err) {
        res.status(500).json({
          success: false,
          message: "Unable to create a post, please try again",
        });
      }
    }
  } catch (error: any) {
    return res.status(400).send(error.message);
  }
}

When i run my next application and call api/post then this error occurs

` OverwriteModelError: Cannot overwrite `Post` model once compiled.
    at Mongoose.model (/Users/mac/Practice/portfolio_projects/ai-image-generation/node_modules/mongoose/lib/index.js:582:13)
    at eval (webpack-internal:///(api)/./src/database/mongodb/models/post.ts:31:62)
    at (api)/./src/database/mongodb/models/post.ts (/Users/mac/Practice/portfolio_projects/ai-image-generation/.next/server/pages/api/post.js:52:1)
    at __webpack_require__ (/Users/mac/Practice/portfolio_projects/ai-image-generation/.next/server/webpack-api-runtime.js:33:42)`

somehow i fixed this issue with some changes in Post.tsx

const Post = mongoose.model("Post") || mongoose.model("Post", PostSchema);

but again when i hit api/post every time this issues occurs

error - MissingSchemaError: Schema hasn't been registered for model "Post".
Use mongoose.model(name, schema)
    at Mongoose.model (/Users/mac/Practice/portfolio_projects/ai-image-generation/node_modules/mongoose/lib/index.js:549:13)

Expected Behavior

application should work this setup

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

mathieuguyot commented 1 year ago

Hello,

For the problem you Have, I manage to get things working replacing your line with:

export const Post = mongoose.models && "Post" in mongoose.models ? mongoose.models.Post : mongoose.model("Post", PostSchema);

Also, make sure that mongoose is not imported inside client components !

And if you are using mongoose 7, I have to put serverComponentsExternalPackages option in next.config.js (even with the fix given here https://github.com/vercel/next.js/issues/42277)

/** @type {import("next").NextConfig} */
module.exports = {
    experimental: { appDir: true, serverComponentsExternalPackages: ["mongoose"] },
    webpack(config) {
        config.experiments = { ...config.experiments, topLevelAwait: true };
        return config;
    }
};

my project https://github.com/mathieuguyot/adventures have mongoose 7 integrated with next 13.2.4, maybe it can help you in any way :)