Search code examples
node.jsmongodbmongoosenext.jsnext.js13

Model not registered while populating data from mongodb in nextjs


I have 3 models USER , CATEGORY , PRODUCT . product model has two fields that refer to other two models . so when i try to populate data it throws an error saying

users isn't registered and same for category

Like if i directly hit the get_all_product endpoint without logging in or collecting category ( mean both user and category model not awaked) it throws error for category and if the category endpoint are hitted then it show me users isn't registered

A way to fix that i found is if i collect data from users collection like user profile and then i collected data from category model like collect all category and hit get all product endpoint it give me all data as expected

But if i try to get it without hitting user or category endpoint its throws an error as i am populating user and category

This is not a good approach as i am building a n ecommerce app where an guest user can explore categories and product so this create a problem for me and always throws an error of users model not registered so what the best fix for this issue here is my models

Category model

import mongoose from "mongoose";
import User from "./User";

const CategorySchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, "Please Provide an Name"],
        unique: true,
    },
    slug: {
        type: String,
        required: [true, "Please Provide an Slug"],
        unique: true,
    },
    image: {
        type: String,
        default: '',
    },
    description: {
        type: String,
        required: [true, "Please Provide an Description"],
    },
    status: {
        type: String,
        default: 'inactive',
        enum: ['active', 'inactive']
    },
    isFeatured: {
        type: Boolean,
        default: false,
    },
    addedBy : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
    },
}, {timestamps: true});

const Category =  mongoose.models.categories || mongoose.model('categories', CategorySchema);
export default Category;

product model

import mongoose from "mongoose";

const ProductSchema = new mongoose.Schema({
    category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'categories',
    },
    name: {
        type: String,
        required: [true, "Please Provide an Name"], 
    },
    slug: {
        type: String,
        required: [true, "Please Provide an Slug"],
    },
    images: {
        type: [String],
        default: [],
    },
    price: {
        type: Number,
        required: [true, "Please Provide an Price"],
    },
    salePrice: {
        type: Number,
        default: 0,
    },
    quantity: {
        type: Number,
        required: [true, "Please Provide an Quantity"],
    },
    description: {
        type: String,
        required: [true, "Please Provide an Description"],
    },
    status: {
        type: String,
        default: 'inactive',
        enum: ['active', 'inactive' , 'rejected']
    },
    isFeatured: {
        type: Boolean,
        default: false,
    },
    addedBy : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'users',
    },
}, {timestamps: true});


const Product =  mongoose.models.products || mongoose.model('products', ProductSchema);
export default Product;

usermodel

import mongoose from "mongoose";


const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, "Please Provide an Name"],
        trim: true,
    },
    email: {
        type: String,
        required: [true, "Please Provide an Email"],
        trim: true,
    },
    password: {
        type: String,
        required: [true, "Please Provide an Password"],
        trim: true,
    },
    phoneNumber: {
        type: String,
        trim : true,
    },
    address: {
        type: String,
        trim : true,
    },
    city: {
        type: String,
        trim : true,
    },
    state: {
        type: String,
        trim : true,
    },
    country: {
        type: String,
        trim : true,
    }, 
    postalCode: {
        type: String,
        trim : true,
    },
    isVerified: {
        type: Boolean,
        default: false,
    },
    image : {
        type: String,
        default: '',
    },
    role : {
        type: String,
        default: 'customer',
        enum : ['admin' , 'vendor' , 'customer']
    },
    isVendorApproved : {
        type: String,
        default: 'pending',
        enum : ['pending' , 'approved' , 'rejected']
    },
    isBlackList : {
        type: Boolean,
        default: false,
    },
    pinCode: {
        type: String,
        default: '',
    },
    resetCode: {
        type: String,
        default: '',
    }

},{timestamps : true})

const User = mongoose.models.users || mongoose.model('users' , userSchema);
export default User;

db connection

import mongoose, { ConnectOptions } from 'mongoose';


interface connectedOptions extends ConnectOptions{
    useNewUrlParser: boolean,
    useUnifiedTopology: boolean,
}

const options: connectedOptions = {
    useNewUrlParser: true,
    useUnifiedTopology: true,
};

const connectDB = async () => {
    const connectionUrl: string = process.env.DB_URI as string;
    mongoose.connect(connectionUrl , options )
        .then(() => console.log(`Database connected successfully`))
        .catch((err) => console.log("Getting Error from DB connection" + err.message))
    mongoose.set('strictQuery', false);
};

export default connectDB;   

product endpoint example

import { NextRequest, NextResponse } from "next/server";
import connectDB from "@/db";
import Product from "@/models/Product";

export const dynamic = 'force-dynamic'

export async function GET(req: NextRequest) {
    connectDB();
    try {
        const product = await Product.find({}).populate('category').populate('addedBy' )
        return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
    } catch (error: any) {
        console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
        return NextResponse.json({ success: false, msg: "something went wrong !" });
    }
}

Note

In mern stack where the mongodb is connected always as app starts, I never face such issues so maybe a consistent connection maybe a good fix

In nextjs db connected only when we hit some endpoint as you can see the route example of products calling connectDB()


Solution

  • Note

    this is just a quick fix but not a permanent solution I am still finding the answer

    One of Quick fix i findout is doing a dummy query to register the Model.This is optimal solution I figured out rather sending 3 request from client side

    import { NextRequest, NextResponse } from "next/server";
    import connectDB from "@/db";
    import Product from "@/models/Product";
    import User from "@/models/User";
    import Category from "@/models/Category";
    
    export const dynamic = 'force-dynamic'
    
    export async function GET(req: NextRequest) {
        connectDB();
        try {
            const getAnyUser =  await User.findOne({});
            const getAnyCategory =  await Category.findOne({});
            const product = await Product.find({}).populate('category').populate('addedBy' )
            return NextResponse.json({ data: product, success: true, msg: "Products Collected successfully" });
        } catch (error: any) {
            console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message)
            return NextResponse.json({ success: false, msg: "something went wrong !" });
        }
    }