Search code examples
mongodbexpressmongooseasync-await

Mongoose: Defining 404 status for not finding a document doesnt work


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


Solution

  • 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 :