9/ Errors

Handling Errors

  • Express has a default error handling

  • Errors can also be thrown too: throw new Error('Pass required');

  • Writing error handlers function(error, req, res, next). If an error is thrown anywhere this function will run

app.use((err, req, res, next)=>{
    console.log('Error');
})
  • define error class in Express
class AppError extends Error{
    constructor (message, status){
        super();
        this.message = message;
        //express default error handling is looking 
        //for a property called status
        this.status = status;
    }
}

module.exports = AppError;
const veryPassaword = (req, res, next)=>{
    const {password} = req.query;
    //http://localhost:3000/?password=test
    if (password === 'test'){
        next();
    }
    throw new AppError('password required', 401);
};
  • destructuring the error
app.use((err, req, res, next)=>{
    const {status = 500, message = 'Something wrong'} = err;
    res.status(status).send(message)
})

Async errors

  • Error caused by looking for an id that does not exist in the database

  • If the function is async, the error handler has to be passed inside the next() parameters

app.get('/products/:id', async (req, res, next) => {
  const {id} = req.params;
  const product = await Product.findById(id);
  if (!product){
    return next(new AppError('Product not found', 404));
  }
  //without return this code will still try run (response is given by the error handler 
  //but Cannot read property 'name' of null will be in the node console log)
  res.render('products/show', {product})
})

Errors from Mongoose or Users

  • use try-catch (only with async functions)
app.post('/products', async (req, res, next) =>{
  try{
    const newProduct = new Product(req.body)
    await newProduct.save();
    console.log(newProduct);
    res.redirect(`/products/${newProduct._id}`);
    }
    catch(e){
      next(e);
    }
})
  • use try-catch when throwing an error too:
app.get('/products/:id', async (req, res, next) => {
  try{
    const {id} = req.params;
    const product = await Product.findById(id);
    if (!product){
      throw new AppError('Product not found', 404);
    }
    res.render('products/show', {product})  
  }
  catch(e){
    next(e);
  }
})

Defining an async utility

function wrapAsync(fn) {
  return function(req, res, next){
    fn(req, res, next).catch(e=>next(e));
  }
}

app.get('/products/:id', wrapAsync(async (req, res, next) => {
    const {id} = req.params;
    const product = await Product.findById(id);
    if (!product){
      throw new AppError('Product not found', 404);
    }
    res.render('products/show', {product});
}))
  • Express 5 is able to handle errors in async functions

Differentiating Mongoose errors

  • Errors can be of different categories (Validation error, Cast error…)
app.use((err, req, res, next)=>{
  console.log(err.name);
  next(err);
})
  • we can have some logic for all the errors of a specific category
const handleValidationError = err => {
  //console.log(err);
  return new AppError(`Validation Failed: ${err.message}`,400);
}

app.use((err, req, res, next)=>{
  console.log(err.name);
  if (err.name ==='ValidationError'){
    err = handleValidationError(err); //returns Validation Failed: Validation failed: name: name cannot be blank
  }
  next(err);
})

and in the product schema:

    name: {
        type: String,
        required: [true, 'name cannot be blank']
    },
Written on January 2, 2022