2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Expressでエラーハンドリングを実施する。

Posted at

#アジェンダ
標題通り、Expressのエラーハンドリングの実施方法。githubに挙がっていたソースコードを元にエラーハンドリングを実施

#ソースコード

ディレクトリ構成
src
├── error-handler
   ├── error-code.ts
   ├── error-exception.ts
   ├── error-handler.ts
   └── error-model.ts
└── server.ts
error-code.ts
import express, { NextFunction } from 'express';
import { Request, Response } from 'express';
import { ErrorCode } from './error-handler/error-code';
import { ErrorException } from './error-handler/error-exception';
import { errorHandler } from './error-handler/error-handler';

const app = express();

app.get('/', (req: Request, res: Response) => {
  res.send('Application works!');
});

app.get('/throw-unauthenticated', (req: Request, res: Response, next: NextFunction) => {
  throw new ErrorException(ErrorCode.Unauthenticated);
  // or
  // next(new ErrorException(ErrorCode.Unauthenticated))
});
app.get('/throw-maximum-allowed-grade', (req: Request, res: Response, next: NextFunction) => {
  throw new ErrorException(ErrorCode.MaximumAllowedGrade, { grade: Math.random() });
  // or
  // next(new ErrorException(ErrorCode.MaximumAllowedGrade, { grade: Math.random() }))
});
app.get('/throw-unknown-error', (req: Request, res: Response, next: NextFunction) => {
  const num: any = null;
  // Node.js will throw an error because there is no length property inside num variable
  console.log(num.length);
});

const someOtherFunction = () => {
  const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new ErrorException(ErrorCode.AsyncError));
    }, 1000);
  });
  return myPromise;
};
app.get('/throw-async-await-error', async (req: Request, res: Response, next: NextFunction) => {
  // express 4
  try {
    await someOtherFunction();
  } catch (err) {
    next(err);
    // next line will not work as expected
    // throw err
  }
  // express 5
  // await someOtherFunction();
});

app.use(errorHandler); // registration of handler

app.listen(3000, () => {
  console.log('Application started on port 3000!');
});
error-code.ts
export class ErrorCode {
  public static readonly Unauthenticated = 'Unauthenticated';
  public static readonly NotFound = 'NotFound';
  public static readonly MaximumAllowedGrade = 'MaximumAllowedGrade';
  public static readonly AsyncError = 'AsyncError';
  public static readonly UnknownError = 'UnknownError';
}
error-exception.ts
import { ErrorCode } from './error-code';

export class ErrorException extends Error {
  public status: number = null;
  public metaData: any = null;
  constructor(code: string = ErrorCode.UnknownError, metaData: any = null) {
    super(code);
    Object.setPrototypeOf(this, new.target.prototype);
    this.name = code;
    this.status = 500;
    this.metaData = metaData;
    switch (code) {
      case ErrorCode.Unauthenticated:
        this.status = 401;
        break;
      case ErrorCode.MaximumAllowedGrade:
        this.status = 400;
        break;
      case ErrorCode.AsyncError:
        this.status = 400;
        break;
      case ErrorCode.NotFound:
        this.status = 404;
        break;
      default:
        this.status = 500;
        break;
    }
  }
}
error-handler.ts
import { Request, Response, NextFunction } from 'express';
import { ErrorCode } from './error-code';
import { ErrorException } from './error-exception';
import { ErrorModel } from './error-model';

export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
  console.log('Error handling middleware called.');
  console.log('Path:', req.path);
  console.error('Error occured:', err);
  if (err instanceof ErrorException) {
    console.log('Error is known.');
    res.status(err.status).send(err);
  } else {
    // For unhandled errors.
    res.status(500).send({ code: ErrorCode.UnknownError, status: 500 } as ErrorModel);
  }
};
error-model.ts
export class ErrorModel {
  /**
   * Unique error code which identifies the error.
   */
  public code: string;
  /**
   * Status code of the error.
   */
  public status: number;
  /**
   * Any additional data that is required for translation.
   */
  public metaData?: any;
}

#実行結果
スクリーンショット 2021-12-10 13.49.13.png

#所感
 エラーハンドリングをライブラリで管理できるのは便利。ただ、改善ポイントが複数あるので、個別カスタマイズか、別のライブラリを探してみるのもアリかと。

■改善箇所。
 ・HTTPステータスとcodeが一致しているので、冗長。
 ・障害発生時、一意に確認できるコードを付与すべき({errorCode:'ERR001'})
 ・エラーエクセプションのswitch文が冗長。エラーコードとステータスは一緒に定義した方が管理しやすい。

#参照ソースコード
https://github.com/nisicadmir/nodejs-typescript

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?