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:
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.
type Result<T, E> = Success<T> | Failure<E>;
This object contains:
- either a
value: T
withsuccess: true
andfailed: false
- or an
error: E
withsuccess: false
andfailed: true
The recommended way to use this monad is by using the runCatching()
function:
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);
AsyncResult<T>
​
An extension of Promise<T>
, adding more operators to it, to state your intentions more explicitly.
Example usage:
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);
Promise utilities ​
Most methods on AsyncResult
also exist as higher-order functions, so they can be applied on regular Promise
chains.
For example:
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"));
@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:
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:
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:
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.