Personal library project, FindbyId returns null?

Tell us what’s happening:
When I use the get request at ‘api/books/:id’ it returns null when I run findbyId, despite there being an id that matches. When I looked around online, most people had this issue if the id wasn’t valid, but even after forcing the value to be an objectId, mongoose still returns null.
The route in question is in line 95 of my api file.

Your code so far

My repl is here!
Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36.

Challenge: Personal Library

Link to the challenge:

You can skip the issue you are facing by using mongoose’s findOne method instead of findById and pass in the id you get from req.params as an argument. These two methods are almost identical in everyway except how they handle undefined.

This should do the trick. Here is an example code:

 let bookid = req.params.id;
  let specificBook = await Book.findOne({_id:bookid}).exec();

Here is what mongoose documentation says:

Finds a single document by its _id field. findById(id) is almost* equivalent to findOne({ _id: id }) . If you want to query by a document’s _id , use findById() instead of findOne() .

The id is cast based on the Schema before sending the command.

This function triggers the following middleware.

  • findOne()

Except for how it treats undefined . If you use findOne() , you’ll see that findOne(undefined) and findOne({ _id: undefined }) are equivalent to findOne({}) and return arbitrary documents. However, mongoose translates findById(undefined) into findOne({ _id: null }) .**

1 Like

So that all makes a lot of sense, but I’m already using findById, which is returning null

.get(async function (req, res){
      let bookid = ObjectId(req.params.id);
      let result = await Book.findById(bookid, (err, data)=>{
        if(err){
          console.log('error',err);
          return "error"
        }else if(data === null){
          return 'no book exists'
        }else{
          return data
        }
      })
      
      console.log(result, "found")
      return res.json(result)

This prints null found to the console.

I would guess this is the problem or part of it. You have an await with a callback, and you should use one or the other. Since it looks more like an async/await setup, you probably want something like

let result = await Book.findById(bookid).exec();

Then you should be able to log the result. If you want to detect errors, you can use a try/catch block. If that doesn’t work, try logging req.params.id to make sure it has the value you expect. Finally, you could try

let result = await Book.find({}).exec();

and log that to see if the collection really has the records you think it has.

So that fixed part of it, but now my tests are not working right, I do one test for a bogus id that isn’t in the database, made entirely of 1s, then its supposed to use my other id to try another get request, but nothing is happening, it doesn’t even appear to start that test, which i found out by trying to log to console when test starts. See below

test('Test GET /api/books/[id] with id not in db',  function(done){
        chai.request(server)
        .get('/api/books/'+idbad)
        .end(function(err, res){
          assert.equal(res.status, 200);
          assert.isString(res.body, 'response should be a string');
          assert.equal(res.body, 'no book exists', 'error message should equal');
          done();
        });
      }); 
      test('Test GET /api/books/[id] with valid id in db',  function(done){
        test('Test GET /api/books',  function(done){
          let url = 'api/books/'+id1
          console.log('test started')
          chai.request(server)
          .get(url)
          .end(function(err, res){
            assert.equal(res.status, 200);
            assert.isObject(res.body, 'response should be an object');
            //assert.property(res.body, 'title', 'Book should contain title');
            //assert.property(res.body, '_id', 'Book should contain _id');
            //assert.equal(res.body.title, "functional test book 1 ",'book titles should match');
            done();
        }); 
      });

which gives the output…

GET /api/books => array of books
        ✓ Test GET /api/books (42ms)
      GET /api/books/[id] => book object with [id]
111111111111111111111111
null
nulled
        ✓ Test GET /api/books/[id] with id not in db (41ms)
        1) Test GET /api/books/[id] with valid id in db //THIS LINE IS RED IN OUTPUT
        POST /api/books/[id] => add comment/expect book object with id

and later

1) Functional Tests Routing tests GET /api/books/[id] => book object with [id] Test GET /api/books/[id] with valid id in db:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
      at listOnTimeout (internal/timers.js:549:17)
      at processTimers (internal/timers.js:492:7)

I seriously dont know whats going on with it.

You probably want to replace this outer test(...) with suite(...) and remove the done from that line, or just remove the outer test() altogether. The timeout may be from mocha waiting and not having another done() after the inner test.

Also, the url here is missing a leading slash so it may be missing your route.

You can also log from the routes your are testing to make sure that your test is hitting your route with the input you intend.

thank you so much, I knew it had to be some kind of syntax thing, but I just couldn’t see the forest for the trees you know?