Node.js、TypeScript、Nest.jsで実現する!Nest.jsにおけるInterceptor、Middleware、Filterの違い

2024-07-27

Nest.jsにおけるInterceptor、Middleware、Filterの違い:詳細解説

Nest.jsには、アプリケーションのロジックと機能を拡張するための3つの主要なコンポーネントがあります。

  • Filter:例外処理とエラーハンドリングを管理するために使用されます。予期せぬエラーが発生した場合に、適切なレスポンスを返したり、ログを記録したりするのに役立ちます。
  • Middleware:リクエストを処理する前に実行されるアクションのシーケンスを定義します。ルーティング、リクエスト検証、ボディパーシングなどのタスクに適しています。
  • Interceptor:リクエストとレスポンスのライフサイクル全体で横断的に処理を実行するために使用されます。認証、ロギング、キャッシュなどのタスクに最適です。

それぞれの特徴と具体的なユースケースを以下に詳しく説明します。

Interceptor

Interceptorは、リクエストとレスポンスがアプリケーションスタックを上下する際に実行されるコードの塊です。リクエストの処理前、処理後、エラー発生時など、さまざまなタイミングで実行できます。

主なユースケース

  • パフォーマンスの監視
    リクエストのパフォーマンスを測定し、ボトルネックを特定します。
  • トランザクション管理
    データベーストランザクションを開始、コミット、ロールバックします。
  • キャッシュ
    頻繁にアクセスされるデータのキャッシュを処理します。
  • ロギング
    リクエストとレスポンスに関する情報を記録します。
  • 認証と認可
    ユーザーがリクエストするリソースへのアクセスを許可されているかどうかを確認します。


@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest();
    const startTime = Date.now();

    return next.handle().finally(() => {
      const response = context.switchToHttp().getResponse();
      const elapsedTime = Date.now() - startTime;
      console.log(`Request ${request.url} took ${elapsedTime}ms`);
    });
  }
}

Middleware

Middlewareは、リクエストが処理される前に実行される一連のアクションを定義します。これらのアクションは、リクエストを検査、変更、または中止するために使用できます。

  • コンテンツ圧縮
    レスポンスを圧縮して帯域幅を節約します。
  • CORS設定
    クロスオリジンリソース共有(CORS)ヘッダーを設定します。
  • ボディパーシング
    JSONやフォームエンコードされたデータなどのリクエストボディを解析します。
  • リクエスト検証
    リクエストボディ、パラメーター、ヘッダーの検証を行います。
  • ルーティング
    リクエストを適切なコントローラーとアクションにルーティングします。
@Middleware()
export class LoggerMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`Request received: ${req.url}`);
    next();
  }
}

Filter

  • アラート
    エラーが発生したときに管理者に通知します。
  • ロギング
    エラーの詳細を記録します。
  • 特定のエラー処理
    特定の種類のエラーを処理し、専用のエラーメッセージを返します。
  • グローバルエラーハンドリング
    アプリケーション全体で発生するすべてのエラーを処理します。
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, context: ExecutionContext) {
    const response = context.switchToHttp().getResponse();
    response.status(exception.status).json({
      message: exception.message,
    });
  }
}

使い分けのヒント

  • Filter
    エラー処理とハンドリングが必要な場合に使用します。
  • Middleware
    リクエストを処理する前に実行する必要があるアクションを定義する場合に使用します。
  • Interceptor
    ロジックを横断的に適用する必要がある場合、またはリクエストとレスポンスのライフサイクル全体で処理が必要な場合に使用します。



Interceptor:レスポンスログ

この例では、LoggingInterceptorを使用して、各リクエストの処理にかかった時間をログに記録します。

import { Injectable, NestInterceptor, CallHandler, ExecutionContext } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest<Request>();
    const response = context.switchToHttp().getResponse<Response>();
    const startTime = Date.now();

    return next.handle().finally(() => {
      const elapsedTime = Date.now() - startTime;
      const message = `Request ${request.method} ${request.url} took ${elapsedTime}ms`;
      console.log(message);
      response.header('X-Response-Time', elapsedTime);
    });
  }
}

このInterceptorを使用するには、app.module.tsファイルで以下のコードを追加する必要があります。

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggingInterceptor } from './logging.interceptor';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, LoggingInterceptor],
})
export class AppModule {}

この例では、AuthMiddlewareを使用して、APIエンドポイントへのアクセスを認証します。

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    const authToken = req.headers['authorization'];
    if (!authToken || authToken !== 'secret123') {
      res.status(401).json({ message: 'Unauthorized' });
      return;
    }

    next();
  }
}
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthMiddleware } from './auth.middleware';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
  middleware: [AuthMiddleware],
})
export class AppModule {}

Filter:グローバルエラーハンドリング

この例では、GlobalExceptionFilterを使用して、アプリケーション全体で発生するすべてのエラーを処理します。

import { Injectable, Catch, ExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { HttpException } from '@nestjs/common/exceptions';
import { Request, Response } from 'express';

@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: any, context: ArgumentsHost) {
    const ctx = context.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status = exception instanceof HttpException ? exception.status : 500;
    const message = exception.message || 'Unexpected error';

    response.status(status).json({
      message: message,
      stack: exception.stack,
      path: request.url,
    });
  }
}
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GlobalExceptionFilter } from './global-exception.filter';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, GlobalExceptionFilter],
})
export class AppModule {}



Guard

Guardは、リクエストを処理する前に特定の条件を評価するために使用されます。認証、認可、ロールベースのアクセス制御などのタスクに最適です。

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const hasAuthToken = request.headers['authorization'] === 'secret123';
    if (!hasAuthToken) {
      throw new UnauthorizedException();
    }

    return true;
  }
}

Pipe

Pipeは、リクエストデータまたはレスポンスデータをトランスフォームするために使用されます。データの検証、フォーマット、変換などのタスクに適しています。

@Injectable()
export class TrimBodyPipe implements Pipe {
  transform(value: any): any {
    if (typeof value === 'string') {
      return value.trim();
    }

    return value;
  }
}

Event Sourcing

Event Sourcingは、イベントストリームを使用してアプリケーションの状態を保持するアーキテクチャパターンです。状態変更をイベントとして記録し、それらを再生してアプリケーションの状態を再構築できます。

@Injectable()
export class OrderService {
  async createOrder(order: Order): Promise<void> {
    const orderCreatedEvent = new OrderCreatedEvent(order.id, order.customerId, order.items);
    await this.eventStore.publish(orderCreatedEvent);
  }
}

CQRS

CQRS(Command Query Responsibility Segregation)は、読み取り操作と書き込み操作を分離するアーキテクチャパターンです。これにより、コードをよりモジュール化し、スケーラブルにすることができます。

@Injectable()
export class GetUserQueryHandler {
  handle(query: GetUserQuery): Promise<User> {
    const userId = query.userId;
    return this.userRepository.findOneById(userId);
  }
}

カスタムデコレータ

カスタムデコレータを使用して、独自のロジックやメタデータをクラス、プロパティ、メソッドに追加できます。

@Injectable()
export class AppController {
  @LogMethod()
  async index(): Promise<string> {
    return 'Hello, world!';
  }
}

これらの代替手段は、それぞれ異なるユースケースに適しています。具体的な状況に応じて、適切なツールを選択することが重要です。

Nest.jsは、さまざまなツールとパターンを提供することで、開発者が柔軟かつ効率的にアプリケーションを構築できるようにしています。Interceptor、Middleware、Filterは、アプリケーションのロジック、セキュリティ、エラー処理を管理するための基本的なツールですが、状況に応じてGuard、Pipe、Event Sourcing、CQRS、カスタムデコレータなどの代替手段も検討する必要があります。


node.js typescript nestjs



Node.js入門ガイド

Node. jsは、サーバーサイドのJavaScript実行環境です。つまり、JavaScriptを使ってウェブサーバーやネットワークアプリケーションを開発することができます。Node. js公式サイトからインストーラーをダウンロードします。...


Node.jsのマルチコア活用

Node. jsは、イベント駆動型の非同期I/Oモデルを採用しているため、一般的にシングルスレッドで動作します。これは、CPUの処理能力を最大限に活用するために、ブロックする操作(例えば、ファイルI/Oやネットワーク通信)を非同期的に処理するからです。...


Node.js ファイル書き込み解説

Node. js は、JavaScript をサーバーサイドで実行するためのプラットフォームです。ファイルシステムへのアクセスも可能で、その中でもファイルにデータを書き込む機能は非常に重要です。const fs = require('fs');...


Node.jsでディレクトリ内のファイル一覧を取得する

Node. jsでは、fsモジュールを使用してディレクトリ内のファイル一覧を取得することができます。readdirメソッドは、指定されたディレクトリ内のファイル名とサブディレクトリ名を同期的にまたは非同期的に取得します。同期的な使用注意lstatメソッドはシンボリックリンクのターゲットファイルの情報を取得します。実際のファイルの情報を取得するには、statメソッドを使用します。...


Node.js スタックトレース出力方法

Node. jsでは、エラーが発生した場合にそのエラーのスタックトレースを出力することができます。スタックトレースは、エラーが発生した場所やその原因を特定する上で非常に役立ちます。最も一般的な方法は、エラーオブジェクトの stack プロパティを使用することです。これは、エラーが発生した場所やその呼び出し履歴を文字列として返します。...



SQL SQL SQL SQL Amazon で見る



Node.jsテンプレートエンジンについて

JavaScriptでプログラミングする際、テンプレートエンジンを使用することで、HTMLファイルや他のテキストベースのファイルに動的なコンテンツを埋め込むことができます。Node. jsには、様々なテンプレートエンジンが利用可能です。代表的なテンプレートエンジンには、EJS、Handlebars、Pug(Jade)などがあります。これらのエンジンは、それぞれ異なる構文や機能を持っていますが、基本的には、テンプレートファイルにHTMLの構造を定義し、JavaScriptのコードを使用して動的なデータを埋め込むことができます。


Node.jsでjQueryを使う?

一般的に、jQueryをNode. jsで直接使用することは推奨されません。Node. jsはサーバーサイドでのJavaScript実行を想定しており、ブラウザ環境向けのjQueryの機能は直接利用できないからです。詳細な解説Node. js サーバーサイドでJavaScriptを実行するためのプラットフォームです。ブラウザ環境とは異なり、DOMやブラウザのAPIは直接利用できません。


Node.js の基礎解説

Node. jsは、JavaScriptをサーバーサイドで実行するためのプラットフォームです。つまり、従来ブラウザ上でしか実行できなかったJavaScriptを、サーバー上で実行できるようにする環境を提供します。Node. js JavaScriptを実行するための環境であり、サーバー上で動作します。


Node.js デバッグ入門

Node. js アプリケーションのデバッグは、JavaScript コードのエラーや問題を特定し、解決するためのプロセスです。以下に、一般的なデバッグ手法を日本語で説明します。これを活用して、コードの実行フローを追跡し、問題が発生している箇所を特定します。


Node.js ファイル自動リロード

Node. jsでファイルを自動リロードする方法について、日本語で説明します。最も一般的な方法は、Node. jsのモジュールを使用することです。代表的なモジュールは以下の通りです。supervisor nodemonと同様に、ファイルの変更を検知してプロセスを再起動します。