Middleware | NestJS 【翻訳】

## NestJS公式ドキュメント翻訳

[NestJS公式ドキュメント翻訳](https://qiita.com/mana-vv/items/a658051a5183183c3e50)

## 原文

[Middleware| NestJS - A progressive Node.js web framework](https://docs.nestjs.com/middleware)

## ミドルウェア

ミドルウェアはルートハンドラの**前**に呼び出される関数です。ミドルウェア関数は[リクエスト](http://expressjs.com/en/4x/api.html#req)オブジェクトと[レスポンス](http://expressjs.com/en/4x/api.html#res)オブジェクト、およびアプリケーションのリクエスト/レスポンスサイクルの`next()`ミドルウェア関数にアクセスできます。`next`ミドルウェア関数は、一般に`next`という名前の変数によって示されます。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499074/8832bff9-fb5e-6065-af38-c407d02253ec.png)

Nestのミドルウェアは、デフォルトでは[express](http://expressjs.com/en/guide/using-middleware.html)のミドルウェアと同等です。Express公式ドキュメントによると、以下のようにミドルウェアの機能について説明しています。

> ミドルウェア機能では、次のタスクを実行できます。
> 　　・コードの実行
> 　　・リクエストおよびレスポンスオブジェクトに変更を加える
> 　　・リクエストとレスポンスのサイクルを終了する
> 　　・スタック内の次のミドルウェア関数を呼び出す
> 　　・現在のミドルウェア関数でリクエストとレスポンスのサイクルを終了しない場合、次のミドルウェア関数に制御を渡すために`next()`を呼び出す必要があります。そうしなければリクエストがハングします。

カスタムNestミドルウェアは、関数または`@Injectable（）`デコレータを持つクラスのいずれかに実装します。クラスには`NestMiddleware`インターフェースを実装する必要がありますが、関数に特別な要件はありません。クラスメソッドを使用して簡単なミドルウェア機能を実装することから始めましょう。

```typescript:logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log('Request...');
    next();
  }
}
```

### 依存性の注入

Nestミドルウェアは依存性の注入を完全にサポートしています。プロバイダやコントローラと同様に、同じモジュール内で利用可能な依存関係を注入できます。これも`constructor`を介して行われます。

### ミドルウェアの適用

`@Module（）`デコレータにはミドルウェアを記述する場所がありません。代わりに、モジュールクラスの`configure()`メソッドを使用してセットアップします。ミドルウェアを含むモジュールは、`NestModule`インターフェイスを実装する必要があります。 `AppModule`レベルで`LoggerMiddleware`をセットアップしましょう。

```typescript:app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}
```

上記の例では、以前に`CatsController`内で定義された`/cats`ルートハンドラー用のLoggerMiddlewareをセットアップしました。ミドルウェアの設定時に、ルート`path`とリクエスト`method`を含むオブジェクトを`forRoutes()`メソッドに渡すことにより、特定のリクエストメソッドに対するミドルウェアをにさらに制限することもできます。以下の例では、`RequestMethod`をインポートして、目的のリクエストメソッド型を参照していることに注意してください。

```typescript:app.module.ts
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.GET });
  }
}
```

`configure（）`メソッドは`async/await`を使用して非同期にすることができます（たとえば、`configure()`内で非同期操作の完了を`await`することができます）。

### ルートワイルドカード

パターンベースのルートもサポートされています。たとえば、アスタリスクは**ワイルドカード**として使用され、任意の文字の組み合わせに一致します。

```typescript
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
```

`'ab*cd'`ルートパスは`abcd`、`ab_cd`、`abecd`などに一致します。文字`？`、`+`、`*`、および`（）`はルートパスで使用でき、正規表現のサブセットです。ハイフン（`-`）とドット（`.`）は文字列ベースのパスによって文字通り解釈されます。

### ミドルウェアコンシューマ

`MiddlewareConsumer`はヘルパークラスです。ミドルウェアを管理するためのいくつかの組み込みメソッドを提供します。それらのすべては、[fluent style](https://en.wikipedia.org/wiki/Fluent_interface)で簡単に**連鎖**することができます。`forRoutes()`メソッドは単一の文字列、複数の文字列、`RouteInfo`オブジェクト、コントローラクラス、さらには複数のコントローラクラスを受け取ることができます。ほとんどの場合は、おそらくコンマで区切られた**コントローラ**のリストを渡すだけでしょう。以下は、単一のコントローラを使用した例です。

```typescript:app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller.ts';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController);
  }
}
```

`apply()`メソッドは単一のミドルウェアまたは複数の引数を取り、[複数のミドルウェア](#複数のミドルウェア)を指定できます。

多くの場合、特定のルートをミドルウェアの適用**外**にしたい場合があります。（[関数ミドルウェア](#関数ミドルウェア)を使用するのではなく、これまで行ってきたように）クラスでミドルウェアを定義する場合、`exclude()`メソッドを使用して特定のルートを簡単に除外できます。以下に示すように、このメソッドは除外する`path`と`method`を識別する1つ以上のオブジェクトを取ります。

```typescript
consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats', method: RequestMethod.GET },
    { path: 'cats', method: RequestMethod.POST }
  )
  .forRoutes(CatsController);
```

上記の例では、`LoggerMiddleware`は`exclude()`メソッドに渡される2つを**除く**`CatsController`内で定義されたすべてのルートにバインドされます。`exclude()`メソッドは関数ミドルウェア（クラスではなく関数で定義されたミドルウェア。詳細は以下を参照）では**機能しない**ことに注意してください。さらに、このメソッドはより一般的なルート（ワイルドカードなど）からのパスを除外しません。そのレベルの制御が必要な場合は、パスによって制限するロジックをミドルウェアに直接持たせましょう。たとえば、リクエストURLにアクセスしてミドルウェアロジックを条件付きで適用する必要があります。

### 関数ミドルウェア

私たちが使用している`LoggerMiddleware`クラスは非常に単純です。メンバー、追加のメソッド、依存関係はありません。クラスではなく単純な関数で定義できないのはなぜでしょうか？実は、関数化することができます。このタイプのミドルウェアは、**関数ミドルウェア**と呼ばれます。ロガーミドルウェアをクラスベースから関数ミドルウェアに変換して、その違いを説明します。

```typescript:logger.middleware.ts
export function logger(req, res, next) {
  console.log(`Request...`);
  next();
};
```

`AppModule`内で使用します。

```typescript:app.module.ts
consumer
  .apply(logger)
  .forRoutes(CatsController);
```

ミドルウェアが依存関係を必要としない場合は、よりシンプルな**関数ミドルウェア**を代わりに使用することを検討してください。

### マルチミドルウェア

前述のように、順次実行される複数のミドルウェアをバインドするには`apply()`メソッド内にカンマ区切りのリストを指定するだけです。

```typescript
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
```

### グローバルミドルウェア

ミドルウェアをすべての登録済みルートに一度にバインドする場合、`INestApplication`インスタンスによって提供される`use()`メソッドを使用できます。

```typescript
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
```