Skip to content

API reference / @backpack/error-handling

@backpack/error-handling 🚩 ​

A package with various utilities to simplify error handling.

Installing ​

First, ensure you have set up Nexus as private registry needed to use Backpack.

Then, install the package using npm:

bash
npm install @backpack/error-handling

Result<T, E> ​

A small utility type representing either a success or a failure. Inspired by many other programming languages that offer similar monads.

typescript
type Result<T, E> = Success<T> | Failure<E>;

This object contains:

  • either a value: T with success: true and failed: false
  • or an error: E with success: false and failed: true

The recommended way to use this monad is by using the runCatching() function:

typescript
import { runCatching } from "@backpack/error-handling/result";

const result = runCatching(() => mightFail(), MyCustomError);

if (result.failed) {
  console.warn("Got custom error", result.error);
  return;
}

console.log("Got value", result.value);

See the full API reference

AsyncResult<T> ​

An extension of Promise<T>, adding more operators to it, to state your intentions more explicitly.

Example usage:

typescript
import { AsyncResult } from "@backpack/error-handling/async-result";

const value = await AsyncResult.try(() => mightFail())
  .map((r) => r * 2)
  .onFailure((error) => console.warn("Got error", error))
  .recoverIfInstanceOf(MyCustomError, () => "fallback-1")
  .orElse("fallback-2");

console.log("Got value", value);

See the full API reference

Promise utilities ​

Most methods on AsyncResult also exist as higher-order functions, so they can be applied on regular Promise chains.

For example:

typescript
const value = await promiseTry(() => mightFail())
  .then(map((r) => r * 2))
  .catch(onFailure((error) => console.warn("Got error", error)))
  .catch(recoverIfInstanceOf(MyCustomError, () => "fallback-1"))
  .catch(orElse("fallback-2"));

See the full API reference

@catchErrors() decorator ​

Backpack also offers a decorator which can be applied on methods that return a Promise<T>. This decorator accepts one or more .catch() callbacks, which will be applied in the provided order.

Example:

typescript
import { catchErrors } from "@backpack/error-handling/promises";

class MyService {
  @catchErrors((error) => "fallback")
  public async mightFail(): Promise<string> {
    // ...

    return "foo";
  }
}

All the higher-order functions mentioned above, which are supposed to be used with .catch(), can also be used in combination with the @catchErrors() decorator:

typescript
class MyService {
  @catchErrors(
    onFailure((error) => console.error("Got error", error)),
    recoverIfInstanceOf(MyError, () => "fallback 1"),
    orElse("fallback-2"),
  )
  public async mightFail(): Promise<string> {
    // ...

    return "foo";
  }
}

This also allows us to use Lambda error handlers, such as provided by @backpack/aws-lambda, or by easily creating one yourself:

typescript
export class MyLambda {
  @catchErrors(myErrorHandler())
  public async handle(): Promise<APIGatewayProxyResult> {
    // ...
  }
}

const myErrorHandler = () =>
  catchAll(
    recoverIfInstanceOf(MyCustomError, myCustomErrorConverter()),
    recoverIfInstanceOf(ProblemError, problemErrorConverter()),
    orElseGet((error) =>
      problemResult({
        type: "/problems/internal-server-error",
        title: "Internal Server Error",
        detail: "Something went wrong. 😼",
        status: 500,
      }),
    ),
  );

About decorators

Native ECMAScript decorators are currently in stage 3 of the TC39 standardization process, and are not yet implemented in Node.js.

However, this is not a problem if your code is transpiled. By using Amazon Lambda Node.js CDK construct which uses esbuild under the hood, this is already taken care of.

Modules ​