Koa@2 error handling

sansSpoon picture sansSpoon · Mar 12, 2018 · Viewed 8.1k times · Source

I'm building an API with Koa. I have all my routes in place with koa-router. Each route uses a controller that has all the logic for a given mongoose model. I've read the Koa docs on error-handling and understand the use of awaiting in a try/catch block. There they mention a Default Error Handler should be set at the beginning of the middleware chain. So if I was to have something like the following, I should have resonable error handling for the route at router.get():

const Koa = require('koa');
const Router = require('koa-router');

const app = new Koa();
const router = new Router();

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    err.status = err.statusCode || err.status || 500;
    throw err;
  }
});

router
    .get('/', async (ctx, next) => {
    console.log('Got Route');
    //ctx.body = users;
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000, () => console.log('Koa app listening on 3000'));

If I was to have something slightly more complex at this route, is there any benefit of adding another try/catch inside the route?

router
    .put('/', async function updateOnServer(ctx, next) {
        try {
            await Model.updateOne({
                _id: ctx.params.id,
            }, {
                field1: ctx.request.body.field1,
                $push: { field2: ctx.request.body.field2 },
            }).exec();
        } catch (e) {
            console.log(e);
        }

        await next();
});

Am I just adding redundant error handling here?

Answer

sansSpoon picture sansSpoon · Mar 14, 2018

I re-read the docs on Error Handling and also found this little tip on the Koa Wiki. From that, I've concluded the following:

Koa Error Handling states:

However, the default error handler is good enough for most use cases.

The default error handler in this case is the Koa-built-in error handler. You do not need to include any kind of custom error handling in the code you write. Koa will write out a stack trace along with the error message, etc.

If you want to modify how the error is handled, add something like the suggested middleware at the very beginning of the middleware chain:

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    err.status = err.statusCode || err.status || 500;
    ctx.body = err.message;
    ctx.app.emit('error', err, ctx);
  }
});

If you want to change that again for a specific route, or a special bit of logic, then add another try/catch block at that point (as per my above):

router
    .put('/', async function updateOnServer(ctx, next) {
        try {
            await Model.updateOne({
                _id: ctx.params.id,
            }, {
                field1: ctx.request.body.field1,
                $push: { field2: ctx.request.body.field2 },
            }).exec();
        } catch (e) {
            ctx.status = 418;
            ctx.body = "a custom error message, with nothing really helpful";
        }

        await next();
});