英文:
Model not registered while populating data from mongodb in nextjs
问题
I have 3 models USER, CATEGORY, PRODUCT. The product model has two fields that refer to the other two models. So when I try to populate data, it throws an error saying:
"users isn't registered" and the same for "category".
For example, if I directly hit the "get_all_product" endpoint without logging in or collecting categories (meaning both the user and category models are not accessed), it throws an error for category. If the category endpoint is hit first, then it shows me "users isn't registered."
A way to fix this is if I collect data from the users collection like user profile and then collect data from the category model like collecting all categories and hit the "get_all_product" endpoint, it gives me all data as expected.
However, if I try to get it without hitting the user or category endpoint, it throws an error as I am populating user and category.
This is not a good approach as I am building an ecommerce app where a guest user can explore categories and products. So this creates a problem for me and always throws an error of "users model not registered." So what is the best fix for this issue? Here are my models:
Category model
import mongoose from "mongoose";
import User from "./User";
const CategorySchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please Provide a Name"],
unique: true,
},
slug: {
type: String,
required: [true, "Please Provide a Slug"],
unique: true,
},
image: {
type: String,
default: '',
},
description: {
type: String,
required: [true, "Please Provide a 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 a Name"],
},
slug: {
type: String,
required: [true, "Please Provide a Slug"],
},
images: {
type: [String],
default: [],
},
price: {
type: Number,
required: [true, "Please Provide a Price"],
},
salePrice: {
type: Number,
default: 0,
},
quantity: {
type: Number,
required: [true, "Please Provide a Quantity"],
},
description: {
type: String,
required: [true, "Please Provide a 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;
User model
import mongoose from "mongoose";
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please Provide a Name"],
trim: true,
},
email: {
type: String,
required: [true, "Please Provide an Email"],
trim: true,
},
password: {
type: String,
required: [true, "Please Provide a Password"],
trim: true,
},
// ... (other fields)
}, { 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("Error:", error.message)
return NextResponse.json({ success: false, msg: "Something went wrong!" });
}
}
Note: In a MERN stack where MongoDB is connected always as the app starts, such issues are not usually faced. Consistent connection may be a good fix. In Next.js, the database is connected only when hitting some endpoint, as shown in the product route example.
英文:
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()
答案1
得分: 0
注意
> 这只是一个快速修复,而不是永久解决方案,我仍在寻找答案
其中一个我找到的快速修复是执行一个虚拟查询来注册模型。这是我找到的比从客户端发送3个请求更优的解决方案。
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: "成功收集产品" });
} catch (error: any) {
console.log("🚀 ~ file: route.ts:9 ~ GET ~ error:", error.message);
return NextResponse.json({ success: false, msg: "出现了问题!" });
}
}
请注意,上述代码部分已翻译成中文。
英文:
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 !" });
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论