Search code examples
node.jstypescriptexpresserror-handlingbraintree

Best way to handle mixed synchronous and asynchronous errors in ExpressJS


Using NodeJS 10.13.0, ExpressJS 4.16.4... Got a controller handling a route that looks like this:

import { Request, Response, NextFunction } from 'express';
import braintree from 'braintree';
import config from '../../server.config';

export function index(req: Request, res: Response, next: NextFunction): void {
  if(!config.braintree) throw new Error('Braintree configuration missing.');
  const gateway: any = braintree.connect(config.braintree); // synchronous
  return gateway.clientToken.generate({})
    .then(response => res.send(response.clientToken))
    .catch(next) // operational error unless config.braintree was changed
}

Reading the ExpressJS docs on error handling, I'm wondering if I'm following best practices - throwing an Error for the synchronous portion and passing the error to next() in the catch for the asynchronous portion.

Any recommendations for improvement?


Solution

  • Considering that promises are in use, both synchronous and asynchronous errors can be consistently handled with async function:

    export async function index(req: Request, res: Response, next: NextFunction) {
      try {
        if(!config.braintree)
          throw new Error('Braintree configuration missing.');
    
        const gateway: any = braintree.connect(config.braintree);
        const response = await gateway.clientToken.generate({})
        res.send(response.clientToken);
      } catch (err) {
        next(err);
      }
    }
    

    Since Express doesn't support promises, async function body should be wrapped with try..catch. Considering that try..catch is common for all async middleware functions, it could be moved to a helper:

    const asyncMiddleware = (middleware: RequestHandler) => async (req: Request, res: Response, next: NextFunction) => {
      try {
        await middleware(req, res, next);
      } catch (err) {
        next(err)
      }
    };
    

    Which is used like:

    export const index = asyncMiddleware(async (...) => {...});