Next.js13.5服务端动作中Mongoose Populate触发栈溢出错误排查
Next.js 13.5服务端动作中Mongoose populate导致调用栈溢出问题
在Next.js 13.5的服务端动作里,执行Mongoose的find查询并返回结果时,碰到了Maximum call stack size exceeded(调用栈超出最大值)错误。移除Mongoose的populate方法后错误消失,所以问题根源锁定在populate操作上。
报错信息
Error: Maximum call stack size exceeded Call Stack String.replace <anonymous> _e file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:447398) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:448056) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:449122) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:449122) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:449122) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:449122) Re file:///C:/Users/hossein/homebnb/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (1:449122)
服务端动作代码
import { Types } from "mongoose"; import Reservation from "@/models/Reservation"; interface IParams { listingId?: string; userId?: string; authorId?: string; } export default async function getReservations(params: IParams) { const { listingId, userId, authorId } = params; const query: any = {}; if (listingId) { query.listingId = new Types.ObjectId(listingId); } if (userId) { query.userId = userId; } if (authorId) { query.listing = { userId: authorId }; } console.log("QUERY: " + JSON.stringify(query)); try { const reservations = await Reservation.find(query) .populate({ path: "userId", select: "name" }) .then((p) => { return p; }) .catch((error) => console.log(error)); console.log("REsSSSSS" + reservations); return reservations; } catch (error: any) { throw new Error(error); } }
解决方法
这个问题本质是Mongoose返回的文档是带有内部方法和循环引用的Mongoose对象,Next.js在序列化这些对象时会陷入无限递归,最终导致栈溢出。特别是populate操作引入关联文档后,循环引用的概率会更高。
可以通过以下几种方式解决:
- 使用
.lean()返回纯JS对象:让Mongoose直接返回普通JavaScript对象,避免携带Mongoose的内部属性和方法,这是最直接的解决方案:const reservations = await Reservation.find(query) .populate({ path: "userId", select: "name" }) .lean(); // 新增lean()方法 - 手动序列化清理:如果需要保留部分Mongoose特性,可以手动将结果转换为JSON再解析回对象,强制剥离循环引用:
const reservations = await Reservation.find(query) .populate({ path: "userId", select: "name" }); const plainReservations = JSON.parse(JSON.stringify(reservations)); return plainReservations; - 检查模型双向引用:排查Reservation和User模型是否存在双向关联(比如User模型也关联了Reservation),如果是,需要在populate时严格限制返回字段,或者调整模型设计移除不必要的双向引用。
另外,代码中.then(p => p)属于冗余代码,直接删除即可简化结构。
内容的提问来源于stack exchange,提问作者Hossein Ansari
相关产品推荐
相关产品推荐

