How to include _id in JSON response (Exercise Tracker)

If what you have is working for you, then ignore this comment:

The date check and conversion can easily be accomplished without an external library, by using the toDateString method of JS Date objects. Most Campers do this.

This is my current add code:

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

  // use date provided by user or use current date
  const reqDate = new Date(req.body.date);
  const isValid = moment(reqDate).isValid();
  const date = isValid ? reqDate.toDateString() : currentDate.toDateString();
  User.findByIdAndUpdate(userId, {
    $push: {
      exercises: {
        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
      });
    }
  });
});

I was able to make it so invalid dates entered by doing this, but that last test still fails.

Here’s my log code:

app.get("/api/exercise/log", (req, res) => {
  const userId = req.query.userId;
  const fromDate = (req.query.from) ? new Date(req.query.from) : undefined;
  const toDate = (req.query.to) ? new Date(req.query.to) : undefined;
  const logLimit = (req.query.limit) ? Number(req.query.limit) : undefined;

  const isLeapYear = year => {
    if (year % 4 === 0) {
      if (year % 100 === 0) {
        if (year % 400 === 0) {
          return true;
        }
      }
    }
    return false;
  };

  const isDateValid = date => {
    // more than 29 days in February during leap year
    // or more than 28 days in February during common year
    // is invalid date and a negative date is invalid
    if (isLeapYear(date.getFullYear()) && (date.getMonth() + 1) === 2) {
      if (date.getDate() > 29) {
        return false;
      }
    } else {
      if (date.getDate() > 28) {
        return false;
      }
    }

    if (date.getDate() < 1) {
      return false;
    }

    // more than 31 days in these months is invalid
    // January, March, May, July, August, October, December
    if ((date.getMonth() + 1) === 1 || (date.getMonth() + 1) === 3 || (date.getMonth() + 1) === 7 ||
  (date.getMonth() + 1) === 8 || (date.getMongth() + 1) === 10 || (date.getMonth() + 1) === 12) {
      if (date.getDate() > 31) {
        return false;
      }
    // more than 30 days in these months is invalid
    // April, June, September, October, December
    } else if ((date.getMonth() + 1) === 4 || (date.getMonth() + 1) === 6 ||
  (date.getMonth() + 1) === 9 || (date.getMonth() + 1) === 11) {
      if (date.getDate() > 30) {
        return false;
      }
    }

    if ((date.getMonth() + 1) < 0 || (date.getMonth() + 1) > 12) {
      return false;
    }

    return true;
  };

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

    if (foundUser) {
      if (!logLimit && !fromDate && !toDate) {
        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: foundUser.exercises,
          count: foundUser.exercises.length + 1
        });
      } else {
        let filteredExercises = [];
        if (isDateValid(fromDate) && isDateValid(toDate)) {
          filteredExercises = foundUser.exercises.map(exercise => {
            if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
              return false;
            }
            return true;
          });

          let slicedExercises = [];
          if (logLimit) {
            slicedExercises = filteredExercises.slice(0, logLimit);
          } else {
            slicedExercises = filteredExercises.slice(0);
          }

          res.json({
            username: foundUser.username,
            _id: foundUser._id,
            log: slicedExercises,
            count: slicedExercises.length + 1
          });
        } else {
          console.log("from date and/or to date is/are invalid");
        }
      }
    }
  });
});

I’ll try to test it out on my end too, but I’ll ask here as well: what am I doing wrong here? Please me out.

And can toDateString be used to check if a Date instance is Invalid Date? Because if so, then I can definitely make use of that.

Apparently, something in the code causes the app to crash when I try to visit the log route. I tried to visit the URL https://woolly-mahogany-chauffeur.glitch.me/api/exercise/log?606ef9ada7173c013863960f&1990-01-01&1990-01-02 and it gave me this message:

Site didn't respond
Something in the code caused it to close the connection before providing a response.

If this is your project please visit us at support.glitch.com for assistance.

Here’s the full code:

const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config();
const path = require("path");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const moment = require("moment");

mongoose.connect(process.env.DB_URI, { useNewUrlParser: true, useUnifiedTopology: true });

const Schema = mongoose.Schema;
const userSchema = new Schema({
  username: { type: String, unique: true }, // can't have someone register more than once
  exercises: { type: Array }
});

const User = mongoose.model("user", userSchema);

app.use(cors());
app.use(express.static("public"));
app.get("/", (req, res) => {
  res.sendFile(path.join(`${__dirname}`, "/views/index.html"));
});

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

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

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

    if (foundUser) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id
      });
    } else {
      const user = new User({
        username: username
      });

      user.save((err, user) => {
        if (err) {
          console.log(err);
          return res.status(500).send("unable to add user");
        }
        console.log(`user ${user.username} saved to database!`);

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

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

  // use date provided by user or use current date
  const reqDate = new Date(req.body.date);
  const isValid = moment(reqDate).isValid();
  const date = isValid ? reqDate.toDateString() : currentDate.toDateString();
  User.findByIdAndUpdate(userId, {
    $push: {
      exercises: {
        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
      });
    }
  });
});

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._doc;
      return rest;
    }));
  });
});

app.get("/api/exercise/log", (req, res) => {
  const userId = req.query.userId;
  const fromDate = (req.query.from) ? new Date(req.query.from) : undefined;
  const toDate = (req.query.to) ? new Date(req.query.to) : undefined;
  const logLimit = (req.query.limit) ? Number(req.query.limit) : undefined;

  const isLeapYear = year => {
    if (year % 4 === 0) {
      if (year % 100 === 0) {
        if (year % 400 === 0) {
          return true;
        }
      }
    }
    return false;
  };

  const isDateValid = date => {
    // more than 29 days in February during leap year
    // or more than 28 days in February during common year
    // is invalid date and a negative date is invalid
    if (isLeapYear(date.getFullYear()) && (date.getMonth() + 1) === 2) {
      if (date.getDate() > 29) {
        return false;
      }
    } else {
      if (date.getDate() > 28) {
        return false;
      }
    }

    if (date.getDate() < 1) {
      return false;
    }

    // more than 31 days in these months is invalid
    // January, March, May, July, August, October, December
    if ((date.getMonth() + 1) === 1 || (date.getMonth() + 1) === 3 || (date.getMonth() + 1) === 7 ||
  (date.getMonth() + 1) === 8 || (date.getMongth() + 1) === 10 || (date.getMonth() + 1) === 12) {
      if (date.getDate() > 31) {
        return false;
      }
    // more than 30 days in these months is invalid
    // April, June, September, October, December
    } else if ((date.getMonth() + 1) === 4 || (date.getMonth() + 1) === 6 ||
  (date.getMonth() + 1) === 9 || (date.getMonth() + 1) === 11) {
      if (date.getDate() > 30) {
        return false;
      }
    }

    if ((date.getMonth() + 1) < 0 || (date.getMonth() + 1) > 12) {
      return false;
    }

    return true;
  };

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

    if (foundUser) {
      if (!logLimit && !fromDate && !toDate) {
        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: foundUser.exercises,
          count: foundUser.exercises.length + 1
        });
      } else {
        let filteredExercises = [];
        if (isDateValid(fromDate) && isDateValid(toDate)) {
          filteredExercises = foundUser.exercises.map(exercise => {
            if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
              return false;
            }
            return true;
          });

          let slicedExercises = [];
          if (logLimit) {
            slicedExercises = filteredExercises.slice(0, logLimit);
          } else {
            slicedExercises = filteredExercises.slice(0);
          }

          res.json({
            username: foundUser.username,
            _id: foundUser._id,
            log: slicedExercises,
            count: slicedExercises.length + 1
          });
        } else {
          console.log("from date and/or to date is/are invalid");
        }
      }
    }
  });
});

const listener = app.listen(process.env.PORT || 3000, () => {
  console.log(`Your app is listening on port ${listener.address().port}`);
});

If you can find something wrong from the code, please let me know.

Not quite. The Date constructor returns NaN, if the input date is not correct:

Strings that are unrecognizable or contain out-of-bounds format element values shall cause Date.parse to return NaN.

Hope this clarifies

Okay, thanks.

Did you see my last message, though? Where I mentioned that I got a “Site didn’t respond” error when trying to visit the log URL.

I managed to pass the tests with your app, after removing all of the date-testing logic you have, and replacing it with:

const date = new Date(req.body.date)
    ? new Date(req.body.date).toDateString()
    : new Date().toDateString();

Also, I removed the if (foundUser) logic, and added a return to the response:

if (err) {
      console.log(err);
      res.json({ error: err });
    }

That is all. The workflow is:

  • If Date constructor can parse date provided, then use that
  • If Date constructor cannot parse date provided, then create a current date
  • Ensure date returned is in toDateString format

Hope this helps

So you took out the isDateValid function or did you keep that? And the test for add is passing for me already (that’s where the code

// use date provided by user or use current date
const reqDate = new Date(req.body.date);
const isValid = moment(reqDate).isValid();
const date = isValid ? reqDate.toDateString() : currentDate.toDateString();

is at).

And do you mean take out if (foundUser) from the log route? If so, do I take the code nested inside the if statement out too?

Yes, I removed all of your date checking functions. I mentioned all the date checking code you would need.

Yes. Just remove the check, because it is useless. If there is an error, return.

So in the log route code, the findById method should be called like this?

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

    if (!logLimit && !fromDate && !toDate) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id,
        log: foundUser.exercises,
        count: foundUser.exercises.length + 1
      });
    } else {
      let filteredExercises = [];
      if (isDateValid(fromDate) && isDateValid(toDate)) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
            return false;
          }
          return true;
        });

        let slicedExercises = [];
        if (logLimit) {
          slicedExercises = filteredExercises.slice(0, logLimit);
        } else {
          slicedExercises = filteredExercises.slice(0);
        }

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: slicedExercises,
          count: slicedExercises.length + 1
        });
      } else {
        console.log("from date and/or to date is/are invalid");
      }
    }
  });

And I remove the date-checking function from the log route and replace it with the code you gave? No need to do anything in the add route, right?

I already added return in front of res.json({ error: err }); by the way.

And I’d take out the if-checks for to and from dates too.

I tried this code:

const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config();
const path = require("path");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const moment = require("moment");

mongoose.connect(process.env.DB_URI, { useNewUrlParser: true, useUnifiedTopology: true });

const Schema = mongoose.Schema;
const userSchema = new Schema({
  username: { type: String, unique: true }, // can't have someone register more than once
  exercises: { type: Array }
});

const User = mongoose.model("user", userSchema);

app.use(cors());
app.use(express.static("public"));
app.get("/", (req, res) => {
  res.sendFile(path.join(`${__dirname}`, "/views/index.html"));
});

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

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

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

    if (foundUser) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id
      });
    } else {
      const user = new User({
        username: username
      });

      user.save((err, user) => {
        if (err) {
          console.log(err);
          return res.status(500).send("unable to add user");
        }
        console.log(`user ${user.username} saved to database!`);

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

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

  // use date provided by user or use current date
  const reqDate = new Date(req.body.date);
  const isValid = moment(reqDate).isValid();
  const date = isValid ? reqDate.toDateString() : currentDate.toDateString();
  User.findByIdAndUpdate(userId, {
    $push: {
      exercises: {
        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
      });
    }
  });
});

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._doc;
      return rest;
    }));
  });
});

app.get("/api/exercise/log", (req, res) => {
  const userId = req.query.userId;
  const fromDate = (req.query.from) ? new Date(req.query.from) : undefined;
  const toDate = (req.query.to) ? new Date(req.query.to) : undefined;
  const logLimit = (req.query.limit) ? Number(req.query.limit) : undefined;

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

    if (!logLimit && !fromDate && !toDate) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id,
        log: foundUser.exercises,
        count: foundUser.exercises.length + 1
      });
    } else {
      let filteredExercises = [];
      if (moment(fromDate, "YYYY-MM-DD", true).isValid() &&
          moment(toDate, "YYYY-MM-DD", true).isValid) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
            return false;
          }
          return true;
        });

        let slicedExercises = [];
        if (logLimit) {
          slicedExercises = filteredExercises.slice(0, logLimit);
        } else {
          slicedExercises = filteredExercises.slice(0);
        }

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: slicedExercises,
          count: slicedExercises.length + 1
        });
      }
    }
  });
});

const listener = app.listen(process.env.PORT || 3000, () => {
  console.log(`Your app is listening on port ${listener.address().port}`);
});

The last test still fails for me. The rest of the tests all pass.

Okay, I finally got it. This is my passing log route:

app.get("/api/exercise/log", (req, res) => {
  const userId = req.query.userId;
  const fromDate = (req.query.from) ? new Date(req.query.from) : undefined;
  const toDate = (req.query.to) ? new Date(req.query.to) : undefined;
  const logLimit = (req.query.limit) ? Number(req.query.limit) : undefined;

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

    if (!logLimit && !fromDate && !toDate) {
      res.json({
        username: foundUser.username,
        _id: foundUser._id,
        log: foundUser.exercises,
        count: foundUser.exercises.length + 1
      });
    } else if (logLimit && fromDate && toDate) {
      let filteredExercises = [];
      if (moment(fromDate, "YYYY-MM-DD", true).isValid() &&
          moment(toDate, "YYYY-MM-DD", true).isValid) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
            return false;
          }
          return true;
        });

        let slicedExercises = [];
        if (logLimit) {
          slicedExercises = filteredExercises.slice(0, logLimit);
        } else {
          slicedExercises = filteredExercises.slice(0);
        }

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: slicedExercises,
          count: slicedExercises.length + 1
        });
      }
    } else if (!logLimit) {
      let filteredExercises = [];
      if (moment(fromDate, "YYYY-MM-DD", true).isValid() &&
          moment(toDate, "YYYY-MM-DD", true).isValid) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date >= fromDate && exercise.date <= toDate)) {
            return false;
          }
          return true;
        });

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: filteredExercises,
          count: filteredExercises.length + 1
        });
      }
    } else if (!fromDate) {
      let filteredExercises = [];
      if (moment(toDate, "YYYY-MM-DD", true).isValid) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date <= toDate)) {
            return false;
          }
          return true;
        });

        let slicedExercises = [];
        if (logLimit) {
          slicedExercises = filteredExercises.slice(0, logLimit);
        } else {
          slicedExercises = filteredExercises.slice(0);
        }

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: slicedExercises,
          count: slicedExercises.length + 1
        });
      }
    } else if (!toDate) {
      let filteredExercises = [];
      if (moment(fromDate, "YYYY-MM-DD", true).isValid()) {
        filteredExercises = foundUser.exercises.map(exercise => {
          if (!(exercise.date >= fromDate)) {
            return false;
          }
          return true;
        });

        let slicedExercises = [];
        if (logLimit) {
          slicedExercises = filteredExercises.slice(0, logLimit);
        } else {
          slicedExercises = filteredExercises.slice(0);
        }

        res.json({
          username: foundUser.username,
          _id: foundUser._id,
          log: slicedExercises,
          count: slicedExercises.length + 1
        });
      }
    }
  });
});