Yeri's Digital Note

Catch multiple exception in javascript with union type

June 15, 2019

Javascript doesn’t have built-in method for catching different types of exception.

But, first of all, I should mention that javascript do have some built-in exception types. And we can check for its instance type to handle different case of errors. If we need custom error for our app, we can also define our own error type.

That being said, there is another way we can workaround this, using union type. Union type is a way to represent a type that can hold several different value at a time. Its like enum in some other language (javascript doesn’t have enum keyword), but simply better, because each type can hold additional value (or not).

We will use a library called union-type to demonstrate this.

First, we create our custom error type.

const Type = require('union-type')

const Err = Type({
  InvalidParam: [String],
  ValidationError: [String, Object],
  UnknownError: [],
})

We created Err custom error type. It has three possible value, the error can be either InvalidParam, ValidationError, or UnknownError.

Each one of them can hold additional value, for example the InvalidParam can hold one additional value of type String, that we can use to pass in the error message.

The ValidationError has two values, first is of type String to pass in error message and the second is of type Object to pass in the validation error details.

Let say the UnknownError is a generic error and doesn’t have additional info, so we pass empty array [].

To create one of those errors, we use the constructor function provided by union-type library inside the Err object.

The name of the constructor function is the same as the error type. The function can also receive argument or not, depending whether the error type has additional value attached to it.

For example we have a function for updating user profile.

async function updateProfile(userId, payload) {
  if (!validId(userId)) {
    throw Err.InvalidParam('Invalid param')
  }

  if (!validPayload(payload)) {
    throw Err.ValidationError('Validation Error', {
      name: 'Name is required',
      email: 'Email format is invalid',
    })
  }

  return await db.updateProfile(userId, payload).catch(_ => {
    // whoops, we don't know what caused the error
    throw Err.UnknownError()
  })
}

In javascript we can throw any object to raise an exception, so we just throw our custom error. So, the updateProfile will either throw InvalidParam, ValidationError, UnknownError if any error occurs, or returns the updated profile if success.

Now we need a way to act on the result of updateProfile accordingly. The Err have a method called case, used to kind of switch..case all the possible values.

For example, we can use the function like so:

async function routeHandler(req, res) {
  try {
    const userId = req.params.userId
    const payload = req.body
    const updated = await updateProfile(userId, payload)

    res.status(200).json(updated)
  } catch (error) {
    // handle different errors appropriately
    Err.case({
      InvalidParam: message => res.status(400).json({message}),
      ValidationError: (message, errors) =>
        res.status(422).json({message, errors}),
      UnknownError: () => res.status(500).json({message: 'Unknown Error'}),
    })(error)
  }
}

Nice and clean!

Also, be sure to check out the documentation of union-type for more info.