3

I,m learning MongoDB and mongoose and now I have a problem in defining a 404 status for my route handler. Here is the code:

app.get('/users/:id', async (req, res) => {
const _id = req.params.id

try {
    const user = await User.findById(_id)
    if (!user) {
        return res.status(404).send()
    }
    res.send(user)
} catch (error) {
    res.status(500).send()
}

})

Now if I give it an id that doesn't exist, it doesn't give me 404 Not Found status. it only executes the catch block which is not what I want. I would appreciate it if you tell me where I made mistake or tell me a way to get error handling for that. Thanks

Ali Raisi
  • 41
  • 6
  • Could you provide the value of req.params.id when the error occurs, and console.log the error in the catch block ? – Đăng Khoa Đinh Apr 24 '21 at 19:00
  • As you said I logged the error to the console. This appeared in the console: CastError: Cast to ObjectId failed for value "6082d50a2c89db3164" at path "_id" for model "User" at processTicksAndRejections (internal/process/task_queues.js:93:5) { messageFormat: undefined, stringValue: '"6082d50a2c89db3164"', kind: 'ObjectId', value: '6082d50a2c89db3164', path: '_id', reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters – Ali Raisi Apr 24 '21 at 20:02

1 Answers1

1

The problem

As you can see in the log

CastError: Cast to ObjectId failed for value "6082d50a2c89db3164" at path "_id" for model "User"

It means : the value you provide to findById function ("6082d50a2c89db3164") is not a valid ObjectId.Then the catch block is executed.

Suggestion

1. Validate the parameter before query in database

I understand that you're trying to provide some id that doesn't exist in the database to test. But IMHO, there a difference between 2 cases :

  • you provide a valid id, and this id cannot be found in the database. It should return 404 in this case
  • you provide an invalid id in the request, it could be a string like "6082d50a2c89db3164", or even "#Q*&$(#@*" or anything we could imagine. For this case, it could be better if we validate the input (req.params._id) to ensure that the format is valid. The code will be something like this:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;

// validate params
if(!isValidateObjectId(_id)) { // the function we need to write
   res.status(200).send("Invalid params"); // you can define your status and message
   return;
}

// good params, get user from database
try {
    const user = await User.findById(_id)
    if (!user) {
        return res.status(404).send()
    }
    res.send(user)
} catch (error) {
    res.status(500).send()
}
})

2. Use findOne() method instead of findById

If you want a simpler solution, don't use findById because the function expects a valid ObjectId. We can use findOne() method :

app.get('/users/:id', async (req, res) => {
const _id = req.params.id

try {
    const user = await User.findOne({_id : _id})
    if (!user) {
        return res.status(404).send()
    }
    res.send(user)
} catch (error) {
    res.status(500).send()
}
})

(IMHO, the first solution is better though..)

Some helpful link :

Đăng Khoa Đinh
  • 5,038
  • 3
  • 15
  • 33
  • You are completely right. The id that I provided to test was invalid. It only accepts id that have 12 cahracters but I mistakenly provided an id with less than 12 characters so it immediately executed the catch block. Thank you so much for the help, it really helped especially the validation code! – Ali Raisi Apr 24 '21 at 20:35