npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@liuqiang1357/catch-decorator-ts

v0.0.4

Published

Handle your exceptions elegantly

Downloads

3

Readme

Catch Decorator TS

Description

Typescript decorator for handling your exceptions elegantly.

Install

npm install catch-decorator-ts

If you use Typescript enable experimentalDecorators flag inside your tsconfig file, otherwise for babel use one of the following plugins babel-plugin-transform-decorators-legacy or @babel/plugin-proposal-decorators.

Usage

Writing code that correctly handles all kinds of errors is hard. Developers usually wrap methods with try/catch block in order to handle different kinds of errors. Now imagine as number of anticipated errors increases, it would mess out with the codebase readability, right? That's why it feels natural to use decorators in order to separate method logic from error handling logic.

// Handler for `Catch` or `DefaultCatch` decorators
// err - error thrown from decorated method
// context - `this` value from decorated method
// args - spreaded arguments from decorated method
type Handler = (err: any, context: any, ...args: any) => any;

// Handler will be executed if decorated method has thrown an error which is instance of `ErrorClassConstructor`
@Catch(ErrorClassConstructor: Function, handler: Handler)

// Handler will be executed no matter what type of error has been thrown
@DefaultCatch: (handler: Handler)

Consider the following example:

class User {
  private repository;

  async getUser(id) {
    try {
      const user = await this.repository.fetch(id);
      return user;
    } catch (error) {
      if (error instanceof DatabaseError) {
        console.log('DatabaseError');
      } else if (error instanceof ConnectionError) {
        console.log('ConnectionError');
      } else {
        console.log('UnrecognizedError');
      }
    }
  }
}

Now let's rewrite the previous example using the introduced decorator. Code remains semantically the same, but more clear for other developers to read it.

import { Catch, DefaultCatch } from 'catch-decorator';

class User {
  private repository;

  constructor() {
    this.getUser = this.getUser.bind(this);
  }

  @DefaultCatch((err, ctx, id) => console.log('UnrecognizedError'))
  @Catch(ConnectionError, (err, ctx, id) => console.log('ConnectionError'))
  @Catch(DatabaseError, (err, ctx, id) => console.log('DatabaseError'))
  async getUser(id) {
    const user = await this.repository.fetch(id);
    return user;
  }
}

Remember when stacking decorators that their execution is going to happen in reverse order.

@DefaultCatch(quxHandler)
@Catch(Foo, fooHandler)
@Catch(Bar, barHandler)
@Catch(Baz, bazHandler)
someRandomMethod(){
  throw new QuxError("oops");
}

Which means that the handlers above are going to execute in following order:

bazHandler -> barHandler -> fooHandler -> quxHandler

Also, it is required to put the DefaultCatch decorator at the very top, otherwise all the Catch decorators above him are not going to get executed as the handler from DefaultCatch will be called nevertheless of the error type. Of course, decorating method with DefaultCatch is optional.

It is also possible to define error handlers as external variables or bind them as class's static methods.

import { Catch, DefaultCatch, Handler } from 'catch-decorator';

const connectionErrorHandler: Handler = (err, ctx, id) =>
  console.log('ConnectionError');
const databaseErrorHandler: Handler = (err, ctx, id) =>
  console.log('DatabaseError');

class User {
  private repository;

  constructor() {
    this.getUser = this.getUser.bind(this);
  }

  @DefaultCatch(User.defaultErrorHandler)
  @Catch(ConnectionError, connectionErrorHandler)
  @Catch(DatabaseError, databaseErrorHandler)
  async getUser(id) {
    const user = await this.repository.fetch(id);
    return user;
  }

  static defaultErrorHandler: Handler = (err, ctx, id) =>
    console.log('UnrecognizedError');
}

Example use case with Express.js

Passing express middleware parameters to error handler.

import { Catch, DefaultCatch, Handler } from 'catch-decorator';

const defaultErrorHandler: Handler = (err, ctx, req, res, next, id) =>
  next(err);
const databaseErrorHandler: Handler = (err, ctx, req, res, next, id) =>
  res.send({ error: err });

class UserController {
  private repository;

  constructor() {
    this.load = this.load.bind(this);
  }

  @DefaultCatch(defaultErrorHandler)
  @Catch(DatabaseError, databaseErrorHandler)
  async load(req: Request, res: Response, next: NextFunction, id: string) {
    const user = await this.userRepository.fetch(id);
    res.send({ user });
  }
}

Or if error handler is reused across multiple methods we can write something like this:

import { Catch, DefaultCatch, Handler } from 'catch-decorator';

const TryCatch = () =>
  DefaultCatch((err, ctx, req, res, next, id) => {
    res.send({ error: err });
    // or
    next(err);
  });

class UserController {
  private repository;

  constructor() {
    this.load = this.load.bind(this);
  }

  @TryCatch()
  async load(req: Request, res: Response, next: NextFunction, id: string) {
    const user = await this.userRepository.fetch(id);
    res.send({ user });
  }
}