In efforts to reduce redundancy, I've been abstracting certain operations into Higher Order Functions (HOF). A HOF is a function which receives a function as an argument or returns a function. Let's look at a HOF which I often use to handle errors during Axios requests.

First, let's define an asynchronous function to get users from a URL:

let getUsers = async () => {
    let users = await axios.get('...')
    
    return users.data
}

"Wait! What about error handling?!", you say. This is where the HOF comes in. Let's define it:

const handleError = fn => {
    return (...params) => {
        return fn(...params).catch(err => console.log(`Error caught!`, err))
    }
}

As you can see, the HOF takes in a function as an argument, attaches a .catch onto it, then returns the function. Additionally, it unpacks the function's parameters using the spread syntax (...params) so you won't lose any original functionality.

Great! Now that we've set up everything, let's see it in action:

Promise.resolve(handleError(getUsers)())
    .then(result => console.log(result))

Notice the placement of the parenthesis! Make sure you don't call getUsers but instead pass it to handleError as an argument, then call handleError!

Boom! We've now got built in error handling which is super reusable and scales well. The possibilities for what you can do with err are endless - it's up to you! Big thanks to Wes Bos for helping me find the value in this approach.