Search code examples
javascriptnode.jsmongodbmongoose

How to get clean data objects from mongoose to merge with other objects in express route


I am using express with typescript. On the login function I get the googleId from the user DB document, I get the shift created by the google id (author) and also need to set a new key value (isAuthenticated: true).

All these objects needs to be merged into the API response. The code:

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);
  }
});

What I tried:

  1. I tried object.assign:
const response = Object.assign({} , req.user, shift, { isAuthenticated: true });
  1. Spread operator
let response = {
      ...req.user,
      ...shift,
      ...{ isAuthenticated: true }
    }

I get e response with alot of symbold like:

{
  '$__': 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
}


Solution

  • Based on your code I assume you are using Mongoose to find and store data in a MongoDB database. Before you merge your shift result into your final response object, you should call .toObject() or .toJSON() on it, possibly with further options depending on your needs.

    The reason why this is necessary is because Mongoose uses its own model class to store the results, including a lot of internal state and metadata (as you see in your example code, e.g. $__ or $isNew). To get a simple and clean object that you are looking for, you would have to call one of the two mentioned instance methods (see the links above) of your shift document like so:

    let shift = await ShiftModel.findOne({ requiredByUserId: googleId });
    
    shift = shift.toJSON(); // or shift.toObject();
    

    Alternatively, the data you are interested in (the actual data of your document) is stored under shift._doc, so you could just do

    let response = {
      ...req.user,
      ...shift._doc,
      ...{ isAuthenticated: true }
    }
    

    as well, while using the approach with .toObject() or .toJSON() would probably be the better way.