Why don't my exercise api test pass?

Tell us what’s happening:
I have everything passing except the two test at “/api/exercise/log”, which I don’t understand because my return looks identical to the examples as for as I can tell.
What am I missing?
Here’s a link to all my replit code:
https://replit.com/@dletulle/boilerplate-project-exercisetracker
Your project link(s)

solution: https://boilerplate-project-exercisetracker.dletulle.repl.co

app.get("/api/exercise/log", (req, res) => {
  User.findById(req.query.userId, (err, user) => {
    if (err) return res.send(err.message);
    if (user === null) return res.send("Unknown userId");
    if (err) return res.send(err.message);

    let froom;
    let to;

    req.query.to ? to = new Date(req.query.to).getTime()
    : to = new Date(8640000000000000).getTime();
    req.query.from ? froom = new Date(req.query.from).getTime()
    : froom = new Date(0).getTime();

    const newLog = user.log.filter((arr) => {
      const currentDate = new Date(arr.date).getTime();
      return currentDate >= froom && currentDate <= to})

    let limit = parseInt(req.query.limit);
    !limit ? limit = newLog.length
    : "";

    res.json({ _id: user.id, username: user.username, count: user.count, log: newLog.slice(0, limit)})
  })
})

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36.

Challenge: Exercise Tracker

Link to the challenge:

I recommend you either add some print statements, or check out the network tab in your browser and verify that the response received is actually what you think it is.

When testing your API, the /api/exercise/log sometimes returns nothing for the log property when it should have 1 item.


Edit

How to inspect the network on Chrome:
https://developers.google.com/web/tools/chrome-devtools/network/

How to inspect the network on Firefox:
https://developer.mozilla.org/en-US/docs/Tools/Network_Monitor

1 Like

I’ve just tried viewing in the network console and the results look fine from what I can see. (unless I’m missing something)
Can you give me an example of when it returns an empty log that should contain 1 item? I’m messing around with it, but I can’t reproduce what you’re saying is happening.

Edit: Also, it passes the last log test actually when req.queries are used.

You can add from , to and limit parameters to a /api/exercise/log request to retrieve part of the log of any user. from and to are dates in yyyy-mm-dd format. limit is an integer of how many logs to send back.

But fails the other 2, which seems odd…

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.

A request to a user’s log ( /api/exercise/log ) returns an object with a count property representing the number of exercises returned.

The screenshot I attached to the previous comment was the example. ^-^’

I’m starting this by subjectively critiquing code I happen to look at, since reviewing/debugging code is always easier with cleaner code.

Comments for Ternaries

Your ternaries can be written a bit simpler, which one you prefer is 100% your choice, it’s a bit subjective.

req.query.from ? to = new Date(req.query.from).getTime()
  : from = new Date(0).getTime();
const { to, from, limit} = req.query;

start= from ? new Date(from).getTime() : new Date(0).getTime();
const { to, from, limit} = req.query;;

start = (from ? new Date(from) : new Date(0)).getTime();
const { to, from, limit} = req.query;;

start = new Date((from ? from : 0)).getTime();

Also, you’re doing things like this occasionally which just looks really weird.

!limit ? limit = newLog.length
      : "";

This is saying if limit is false, set limit to newLog.Length else do nothing?

let limitValue = parseInt(limit);

if (!limitValue)
  limitValue = newLog.length;

Multiple Conditionals

if (err) return res.send(err.message);
if (user === null) return res.send("Unknown userId");
if (err) return res.send(err.message);

You’re handling the same condition twice here, perhaps remove the bottom one?

The Real Problem

It looks to me like you are rejecting calls on the /api/exercise/add endpoint which are valid.

For example, this request returns Invalid Date, but it’s 100% valid. The task says that if a date is not specified, default to the current date.

As you aren’t adding these to the user, the user doesn’t have the correct log property value when requesting the user.

1 Like

Sorry, I’m still not understanding…
In the image you just showed, the userId you use doesn’t exist in my database. If I type into the form exactly what your images form data shows, I’m getting back “Unknown userId”, which is correct, as that userId doesn’t exist.

When I submit a form with a userId that does exists and leave the date field empty, it does default to the current date as it should. This makes sense because I pass the test to the /api/exercise/add endpoint.

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 don’t understand the differences we’re getting. What exactly do I need to input into my form to reproduce the response you’re getting? Because I’m only getting correct responses from my testing.

This is my last project to get the certification, so I really want to understand what’s wrong…

Ahh, sorry that I didn’t make it clear.

I wasn’t filling out your form, I’m looking at the tests being executed on the client side when you submit your project.


Closing in on the key issue, if you only test using your form that leads to a problem. Who says that users that consume your API will only ever do so from your form?

When using your form, a date field is always specified, and default to the value of an empty string. Sounds fair?

But a client can make requests to your API without specifying a date field.

The following is what the request looks like when done using your form:
image

However, when freeCodeCamp is testing this API endpoint, it’s sending the following:
image

These are entirely separately cases, you correctly handle an empty string, but you don’t handle undefined, which results in Invalid Date instead of defaulting to the current date.

1 Like

Dude, you’re awesome! I passed the test now! Thanks so much for the detailed explanations!

If I may ask one more question though… I’ve had the misunderstanding that a user could only post through the form. How does freecodecamp post to the api without the form?

An API endpoint can be accessed by anything that can execute HTTP requests.

When you make the API endpoint in the backend, you aren’t in any capacity binding it to the form on the frontend. Anyone can query the backend with full flexibility, and anything sanitized on the frontend should also be sanitized in the backend.

You can limit this through an appropriate CORS configuration, but only browsers will listen to that, a malicious user using an HTTP client can still intentionally input malformed data.

For example, I can use any HTTP client like Insomnia to make requests, and we can make requests to your backend using any programming language too.

In JavaScript (browser), and likely similar to how freeCodeCamp is doing it, I could query your API using something like the following:

const url = 'https://boilerplate-project-exercisetracker.dletulle.repl.co/api/exercise/add';
const requestBody = {
  userId: null,
  description: 238424,
  duration: "The form won't accept a string, but doesn't mean I can't send one if I don't use your form."
}

fetch(url, { method: 'POST', body: requestBody })
  .then((res) => res.text())
  .then((data) => console.log(data));

I’m now interacting with your API endpoint in JavaScript, providing it unexpected data in every field.

In Insomnia:


Edit

ahem… Actually, the form does accept a string for the duration property. But if the form on the frontend was validating the field, so it was only numbers, that doesn’t mean the backend will never receive strings. People using other clients can send whatever they want and the backend needs to be ready to accept any weirdness.

1 Like

That makes sense and seems very important to know. :sweat_smile: Thanks again!