You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Mongoose获取干净数据对象并在Express路由中合并

问题

我正在用Express搭配TypeScript开发登录功能,流程是从用户数据库文档中获取googleId,再用这个googleId查询对应的shift记录,最后要把req.user、shift记录和isAuthenticated: true合并成干净的API响应返回。但用Object.assign和扩展运算符合并后,响应里包含了大量Mongoose内部属性(比如$__、_doc),得不到纯净的合并对象。

相关代码:

import ShiftModel from '../../models/Shift.model';
userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { googleId }: any = req.user;
    const shift = await ShiftModel.findOne({ requiredByUserId: googleId });

    // 尝试过的合并方式
    // const response = Object.assign({} , req.user, shift, { isAuthenticated: true });
    let response = {
      ...req.user,
      ...shift,
      ...{ isAuthenticated: true }
    }
    console.log(response);

    res.status(200).json(req.user);
  } catch (error) {
    next(error);
  }
});

合并后得到的响应示例:

{
  '$__': InternalCache {
    activePaths: StateMachine { paths: [Object], states: [Object] },
    skipId: true
  },
  '$isNew': false,
  _doc: {
    _id: new ObjectId("6501bc166242e72ea25448f3"),
    days: [ 'monday', 'tuesday', 'thursday', 'friday' ],
    available: true,
    shiftPattern: 'days',
    startHour: '08:00',
    endHour: '17:30',
    requiredByUserId: '115426204517705247377',
    dateCreated: 2023-09-13T13:39:57.561Z,
    __v: 0
  },
  isAuthenticated: true
}

解决方法

问题核心是req.user和shift都是Mongoose的Document实例,并非普通JavaScript对象,直接扩展会带上Mongoose的内部属性。以下是几种获取纯净对象的可行方案:

方案1:用.toObject()转换Mongoose文档

将Mongoose实例转换为普通JS对象后再合并:

userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { googleId }: any = req.user;
    const shift = await ShiftModel.findOne({ requiredByUserId: googleId });

    // 转换为普通JS对象,兼容req.user不是Mongoose实例的情况
    const userObj = typeof req.user.toObject === 'function' ? req.user.toObject() : req.user;
    const shiftObj = shift && typeof shift.toObject === 'function' ? shift.toObject() : {};

    const response = {
      ...userObj,
      ...shiftObj,
      isAuthenticated: true
    };

    res.status(200).json(response);
  } catch (error) {
    next(error);
  }
});

方案2:查询时用.lean()直接获取普通对象

在Mongoose查询语句后添加.lean(),查询结果直接返回纯净的JS对象,无需额外转换:

userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { googleId }: any = req.user;
    // 添加.lean(),直接返回普通JS对象
    const shift = await ShiftModel.findOne({ requiredByUserId: googleId }).lean();

    // req.user如果是Mongoose实例仍需转换,根据你的认证中间件情况调整
    const userObj = typeof req.user.toObject === 'function' ? req.user.toObject() : req.user;

    const response = {
      ...userObj,
      ...(shift || {}),
      isAuthenticated: true
    };

    res.status(200).json(response);
  } catch (error) {
    next(error);
  }
});

方案3:提取_doc属性(不推荐)

Mongoose文档的实际数据存储在_doc属性中,可直接提取,但该方式依赖Mongoose内部实现,后续版本可能变动,谨慎使用:

const userObj = req.user._doc || req.user;
const shiftObj = shift?._doc || {};
const response = {
  ...userObj,
  ...shiftObj,
  isAuthenticated: true
};

内容的提问来源于stack exchange,提问作者daniel sas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:50:17