Fastkit

catcher

Custom classes for Type-safe handling of exceptions in your application.

Installation

npm install @fastkit/catcher

Why catcher?

Exceptions occur in applications all the time. There are also times when it is necessary to intentionally throw exceptions.
Either way, these exceptions need to be handled at some final layer, and I would like to see, on a per-package or per-application basis, a consistent schema where the parameters of this exception are abstracted and referenced both when handling the exception and when throwing the exception.
catcher is a builder of exception classes dedicated to your application to meet this need in your application or package development.

Assumed Philosophy

catcher basically assumes the following philosophy. Of course, there is no problem to use it without assuming the following.
  • Handle any exceptions that occur together at a layer as close to the call of the process as possible
  • The service logic at the end of the line, etc., either supplements the exception as little as possible, or supplements it, creates the necessary exception instance, and throws it to the caller.
  • Have rules for exception handling and schemes during constructs by abstracting the exception situation according to the requirements and nature of the application.
    • Requested resource not found.
    • No response was received from the opposing system.
    • There was no authorization for the requested resource.

Basic usage flow

1. Designing Exceptions in Your Application

e.g.
  • All exceptions have a numerical status that follows the HTTP status code, and the status alone can be used to roughly distinguish the status of the exception.
  • Since axios is mainly used in the application, the message and status are motivated when constructs are based on the axios error class

2. Define exception classes according to design

e.g.
import { build, axiosErrorResolver } from '@fastkit/catcher';

type YourAppErrorInput = string | number | { [key: string]: any };

interface YourAppErrorNormalized {
  name: string;
  message: string;
  stack?: string;
  status: number;
}

export class YourAppError extends build({
  resolvers: [axiosErrorResolver],
  normalizer: (resolvedData) => {
    return (input: YourAppErrorInput): YourAppErrorNormalized => {
      let { name, message, status } = resolveYourAppErrorInput(input);
      let stack: string | undefined;
      const { axiosError, nativeError } = resolvedData;
      if (axiosError) {
        name = axiosError.name;
        message = axiosError.message;
        stack = axiosError.stack;
        const { response } = axiosError;
        if (response) {
          status = response.status;
        }
      } else if (nativeError) {
        name = nativeError.name;
        message = nativeError.message;
        stack = nativeError.stack;
        if (nativeError.name === 'PayloadTooLargeError') {
          status = 413;
        }
      }
      const statusResolved = resolveStatusMessage(status);
      status = statusResolved.status;
      if (!message) {
        message = statusResolved.message;
      }
      return {
        name,
        message,
        stack,
        status,
      };
    },
  },
});ts

3. Use custom exceptions you define

e.g.
import { YourAppError } from './error';

export async function someFn() {
  try {
    const result = await axios.get('/some/path/');
    return result.data;
  } catch (_err) {
    const err = YourAppError.from(_err);
    if (err.status === 404) {
      alert('Not found...!!!');
      return;
    }
    throw err;
  }
}ts

build
function

# Signature
<Resolvers extends AnyResolvers, Normalizer extends AnyNormalizer<Resolvers>>(opts: CatcherBuilderOptions<Resolvers, Normalizer>): CatcherConstructor<Resolvers, Normalizer>

Generate exception catcher constructor

  • The instance type is determined by the custom resolver and normalizer specified in the options
# Parameters
NameTypeDefault
opts*CatcherBuilderOptions<Resolvers, Normalizer>
# Return Type
CatcherConstructor<Resolvers, Normalizer>

Exception catcher constructor

CatcherBuilderOptions
class

Catcher constructor generation options

defaultName
optional

undefined | string

Default name if the name cannot be resolved from the supplemented exception

resolvers
optional

undefined | Resolvers

List of Exception Resolver

normalizer

Normalizer

Exception Normalizer

A method to finally normalize the set of values resolved by the resolver

CatcherConstructor
class

Exception catcher constructor

  • The first thing to do is to generate this constructor in your application using the build method
  • Instance properties are extended by type arguments at creation
# Signature
(errorInfo: Parameters<ReturnType<Normalizer>>[0]): Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on the error information.

# Parameters
NameTypeDefault
errorInfo*Parameters<ReturnType<Normalizer>>[0]
# Return Type
Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on the error information.

create
function

# Signature
(errorInfo: Parameters<ReturnType<Normalizer>>[0]): Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on the error information.

# Parameters
NameTypeDefault
errorInfo*Parameters<ReturnType<Normalizer>>[0]
# Return Type
Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on the error information.

from
function

# Signature
(unknownException: unknown, overrides?: Partial<ReturnType<ReturnType<Normalizer>> & ErrorImplements>): Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on an unknown exception. The fields of the error instance can be overwritten by specifying an object as the second argument.

# Parameters
NameTypeDefault
unknownException*unknown
overridesPartial<ReturnType<ReturnType<Normalizer>> & ErrorImplements> | undefined
# Return Type
Catcher<Resolvers, ReturnType<ReturnType<Normalizer>>> & ReturnType<ReturnType<Normalizer>>

Create an error instance based on an unknown exception. The fields of the error instance can be overwritten by specifying an object as the second argument.

Catcher
class

Caught exception instances

isCatcher
readonly

true

It is a catcher instance

data
readonly

CatcherData<T>

Error information fully processed by custom resolvers and normalizers

resolvedData
readonly

UnionToIntersection<ReturnType<MergeParametersAndReturnTypes<[(source: unknown) => NativeErrorOverrides | undefined, ...Resolvers]>>>

Data extracted by custom resolvers

source
readonly
optional

undefined | Catcher<Resolvers, T>

Catcher instance generated from original exception source before override

  • Only set if override is specified at instance creation

histories
readonly

Catcher<Resolvers, T>[]

List of extension sources for own instance

  • The younger the index, the closer the source is to you.

messages
readonly

string[]

全ての歴史を辿ったメッセージのリスト

  • If multiple identical messages are detected, they are filtered to avoid duplicates

toJSON
function

# Signature
(): ErrorImplements & CatcherData<T> & { messages: string[]; }

Obtain as a JSON object

# Parameters
NameTypeDefault
# Return Type
ErrorImplements & T & { $__catcher: true; } & { messages: string[]; }

Obtain as a JSON object

toJSONString
function

# Signature
(indent?: number | boolean): string

Obtain as JSON string

# Parameters
NameTypeDefault
indentnumber | boolean | undefined
number of indentations
# Return Type
string

Obtain as JSON string