Error Handling in express on async functions does not work

Viewed 259

I've been trying to implement Error Handling in Express on async functions with try and catch as well as creating a separate file but when I get the error express doesn't handle it, the site just crashes.

I have tested it on app.get('/campgrounds/:id... I would connect to a fake id to see if the error handler catches it but it doesn't.

The JS file:

const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const Campground = require('./modules/campground');
const engine = require('ejs-mate');
const methodOverride = require('method-override');
const cities =  require('./seeds/cities')
const ExpressError = require('./utilities/ExpressError');
const catchAsync = require('./utilities/catchAsync');

mongoose.connect('mongodb://localhost:27017/yelpcamp');

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => {
    console.log("Database connected");
});

const app = express();

app.engine('ejs', engine);
app.set('view engine','ejs');
app.set('views',path.join(__dirname,'views'));
app.use(express.urlencoded({extended:true}));
app.use(methodOverride('_method'));


app.get('/',(req,res)=>{
 res.redirect('/campgrounds');
})

app.get('/campgrounds',catchAsync(async (req,res)=>{
    const campgrounds = await Campground.find({});
    res.render('campgrounds/index',{campgrounds})
}))

app.get('/campgrounds/new',catchAsync(async (req,res)=>{
    const campgrounds = await Campground.find({});
    res.render('campgrounds/new',{ campgrounds })   
}))
app.post('/campgrounds',catchAsync(async(req,res, next)=>{
    const newCampground = new Campground(req.body);
    await newCampground.save();
    res.redirect(`campgrounds/${newCampground._id}`);
}))  
app.patch('/campgrounds/:id', catchAsync(async (req,res)=>{
    const {id}=req.params;
    const campground = await Campground.findByIdAndUpdate(id, req.body, {runValidators: true, new: true});
    res.redirect(`/campgrounds/${id}`)
}))

app.delete('/campgrounds/:id',catchAsync(async (req,res)=>{
    const { id } = req.params;
    await Campground.findByIdAndDelete(id);
    res.redirect('/campgrounds');
}))
app.get('/campgrounds/:id',catchAsync(async (req,res,next)=>{
    const { id } = req.params;
    const campground = await Campground.findById(id);
    res.render('campgrounds/details',{campground})
}))
app.get('/campgrounds/:id/edit',catchAsync(async(req,res)=>{
    const { id } = req.params;
    const campgrounds = await Campground.find({});
    const campground = await Campground.findById(id);
    res.render('campgrounds/edit',{campground, campgrounds});
}))
app.use((err,req,res,next)=>{
    res.send('Error here!');
})

app.listen(3000,()=>{
    console.log('Listening on port 3000')    
})

This is the separate file called catchAsync:

module.exports = func =>{
    return (req,res,next) =>{
        func(req,res,next).catch(next);
    }
}
1 Answers

Mongoose returns null if it does not find a document using findById. So in your case const campground = await Campground.findById(id); does not throw an error/rejects, instead null is passed as campground to your ejs-template, which probably results in the error you've described.

If you want to actually throw an error and force your error-handler to be invoked, you can throw an error from your /find-handler, e.g.

app.get('/campgrounds/:id',catchAsync(async (req,res,next)=>{
    const { id } = req.params;
    const campground = await Campground.findById(id);
    if(!campground) {
      throw new Error("Campground not found");
    }
    res.render('campgrounds/details',{campground})
}))
Related