How to include _id in JSON response (Exercise Tracker)

What about:

res.json(foundUsers.map(user => { const {_v, ...rest} = user; return rest;}));

Again, you should be debugging this. console.log everything, until you can see what it going on.

Now I have this:

[{"$__":{"strictMode":true,"selected":{},"getters":{},"_id":"604df36384b54c25bc43b475","wasPopulated":false,"activePaths":{"paths":{"_id":"init","username":"init","__v":"init"},"states":{"ignore":{},"default":{},"init":{"_id":true,"username":true,"__v":true},"modify":{},"require":{}},"stateNames":["require","modify","init","default","ignore"]},"pathsToScopes":{},"cachedRequired":{},"session":null,"$setCalled":{},"emitter":{"_events":{},"_eventsCount":0,"_maxListeners":0},"$options":{"skipId":true,"isNew":false,"willInit":true,"defaults":true}},"isNew":false,"$locals":{},"$op":null,"_doc":{"_id":"604df36384b54c25bc43b475","username":"DragonOsman","__v":0},"$init":true}]

Code:

app.get("/api/exercise/users", (req, res) => {
  User.find({}, (err, foundUsers) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUsers.length === 0) {
      res.json({ error: "No users in database" });
    }

    res.json(foundUsers.map(user => {
      const { __v, ...rest } = user;
      console.log(rest);
      return rest;
    }));
  });
});

I need just the _doc part, but when I try

const { __v, _doc, ...rest } = user;

it crashes and I have this error message:

MongooseError: Operation `users.find()` buffering timed out after 10000ms
    at Timeout.<anonymous> (E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:185:20)
    at listOnTimeout (internal/timers.js:554:17)
    at processTimers (internal/timers.js:497:7)
E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\helpers\promiseOrCallback.js:19
            throw error;
            ^

TypeError: Cannot read property 'length' of undefined
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\server.js:62:20
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\model.js:4870:16
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\model.js:4870:16
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\helpers\promiseOrCallback.js:16:11
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\model.js:4893:21
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\query.js:4392:18
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\query.js:4427:14
    at cb (E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\query.js:1897:14)        
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mquery\lib\collection\node.js:27:21       
    at collectionOperationCallback (E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:160:26)
    at Timeout.<anonymous> (E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:185:11)
    at listOnTimeout (internal/timers.js:554:17)
    at processTimers (internal/timers.js:497:7)
Emitted 'error' event on Function instance at:
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\model.js:4872:13
    at E:\programming\visual_studio_code\boilerplate-project-exercisetracker\node_modules\mongoose\lib\helpers\promiseOrCallback.js:16:11
    at processTimers (internal/timers.js:497:7)

I got it by doing:

    res.json(foundUsers.map(user => {
      const { __v, ...rest } = user._doc;
      console.log(rest);
      return rest;
    }));

@Sky020 For the 4th user story:

You can POST to /api/exercise/add with form data userId=_id, description, duration, and optionally date. If no date is supplied, the current date will be used. The response returned will be the user object with the exercise fields added.

I tried this to update the user info in the database

app.post("/api/exercise/add", (req, res) => {
  const { userId, description, duration } = req.body;
  const currentDate = new Date();

  // use date provided by user or use current date
  const date = req.body.date ||
  `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;

  User.findOne({ _id: userId }, (err, foundUser) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUser) {
      User.updateOne({ _id: userId }, {
        username: foundUser.username,
        _id: foundUser._id,
        $set:
        {
          description: description,
          duration: duration,
          date: date
        }
      }, (err, user) => {
        if (err) {
          console.log(err);
          res.json({ error: err });
        }

        res.json({
          username: user.username,
          _id: user._id,
          description: user.description,
          date: user.date
        });
      });
    }
  });
});

I got an empty response back with this, though. What did I do wrong here.

I am going to need more than this. Where do you get an empty response?

Could it be because you are not finding a document so if (foundUser never runs?

Again…use console.log to debug what is going on, and where.

I get an empty JSON object as the response to the GET request.

And the if (foundUser) code does run. I put a console.log call in there and it ran.

Then, what happens in here?

}, (err, user) => {
        if (err) {
          console.log(err);
          res.json({ error: err });
        }

        res.json({
          username: user.username,
          _id: user._id,
          description: user.description,
          date: user.date
        });
      });

First of all, did I call that function correctly? Help me out with that first, please. I did read the docs, but I’m still not clear on whether or not I used the callback correctly.

Yes, that looks about right.

The only thing to watch out for is mentioned in the docs:

Note: conditions is optional, and if conditions is null or undefined, mongoose will send an empty findOne command to MongoDB, which will return an arbitrary document. If you’re querying by _id , use findById() instead.

I’ll go with findByIdAndUpdate then, I think.

I tried this:

app.post("/api/exercise/add", (req, res) => {
  const { userId, description, duration } = req.body;
  const currentDate = new Date();

  // use date provided by user or use current date
  const date = req.body.date ||
  `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;

  User.findOne({ _id: userId }, (err, foundUser) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUser) {
      User.findByIdAndUpdate(userId, {
        username: foundUser.username,
        _id: foundUser._id,
        $set:
        {
          description: description,
          duration: duration,
          date: date
        }
      }, { new: true, useFindAndModify: false }, (err, user) => {
        if (err) {
          console.log(err);
          res.json({ error: err });
        }

        res.json({
          username: user.username,
          _id: user._id,
          description: user.description,
          date: user.date
        });
      });
    }
  });
});

and got this object as the response:

{"username":"DragonOsman","_id":"604df36384b54c25bc43b475"}

Why wasn’t it updated?

Why are you finding one, then finding one again?

With this too:

app.post("/api/exercise/add", (req, res) => {
  const { userId, description, duration } = req.body;
  const currentDate = new Date();

  // use date provided by user or use current date
  const date = req.body.date ||
  `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;

  User.findByIdAndUpdate(userId, {
    $set:
    {
      description: description,
      duration: duration,
      date: date
    }
  }, { new: true, useFindAndModify: false }, (err, foundUser) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUser) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id,
        description: foundUser.description,
        date: foundUser.date
      });
    }
  });
});

I have the same issue: response doesn’t have the updated document.

@Sky020 I changed it to this:

app.post("/api/exercise/add", (req, res) => {
  const { userId, description, duration } = req.body;
  const currentDate = new Date();

  // use date provided by user or use current date
  const date = req.body.date ||
  `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;

  User.findByIdAndUpdate(userId, {
    $set:
    {
      description: description,
      duration: duration,
      date: date
    }
  }, { new: true, useFindAndModify: false }, (err, foundUser) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUser) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id,
        description: description,
        duration: duration,
        date: date
      });
    }
  });
});

But it still doesn’t update the document in the database. What am I doing wrong here?

I do not see this in the docs as an available option.

FYI, $set is automatically used. Nothing wrong with the way you have it, though.

Otherwise, I am unsure why it would not be working.

There’s a deprecation warning that comes up about that if you don’t add it.

What should try logging to see why? Any suggestions?

I believe this is because you didn’t added “description, duration date” to your shema. mongoose save and return only the fields defined in the shema
or you can add option {strict:false}
https://mongoosejs.com/docs/guide.html#strict

Since I’m trying to update the document, shouldn’t the schema be affected? But yeah, okay; I’ll set strict to false.

@Sky020 There’s this page in the docs; it mentions useFindAndModify.

It says:

Mongoose's findOneAndUpdate() long pre-dates the MongoDB driver's findOneAndUpdate() function, so it uses the MongoDB driver's findAndModify() function instead. You can opt in to using the MongoDB driver's findOneAndUpdate() function using the useFindAndModify global option.

and gives this example code:

// Make Mongoose use `findOneAndUpdate()`. Note that this option is `true`
// by default, you need to set it to false.
mongoose.set('useFindAndModify', false);

Anyway, with this I finally got it. Thanks for the help. Will move on to the next user story soon, then.

@Sky020 @yakhousam So for this user story:

You can make a GET request to /api/exercise/log with a parameter of userId=_id to retrieve a full exercise log of any user. The returned response will be the user object with a log array of all the exercises added. Each log item has the description, duration, and date properties.

is it possible to do this while having it so that there can’t be duplicate document with the same username field? And how can I get an array of data on a single user like that?

For now I have this:

app.get("/api/exercise/log", (req, res) => {
  const userId = req.params.userId;

  User.findById(userId, (err, foundUser) => {
    if (err) {
      console.log(err);
      res.json({ error: err });
    }

    if (foundUser) {
      const log = [];
      for (const info of )
    }
  });
});

I’m thinking of trying to get the stuff in a loop and pushing objects into an array that way and returning a JSON array with that data. But I don’t know how to get the array. And I also need to first know if I can do this with my current setup, as well as how to try it.

the data you are looking for is in the collection “exercises”. a simple Exercice.find using the userId as a parameter should return all the exercises by userid

I’m just asking if making the username field a unique one would be bad for this user story. Right now my username field is unique and I’ve also defined my POST code for new-user like this:

app.post("/api/exercise/new-user", (req, res) => {
  const username = req.body.username;

  const user = new User({
    username: username
  });

  User.findOne({ username: username }, (err, foundUser) => {
    if (err) {
      console.log(err);
    }

    if (!foundUser) {
      user.save((err, user) => {
        if (err) {
          console.log(err);
        }
        console.log(`user ${user.username} saved to database!`);
      });
    }

    res.json({
      username: user.username,
      _id: user._id
    });
  });
});

Notice how I only enter in a new user with a given username if one with that username isn’t already present? I want to know if that’s a bad idea for this user story:

You can make a GET request to /api/exercise/log with a parameter of userId=_id to retrieve a full exercise log of any user. The returned response will be the user object with a log array of all the exercises added. Each log item has the description, duration, and date properties.

I may have to allow more than document with the same username, so that multiple exercise records can be added for the same user. Or is there a way to complete this user story without doing that?