【NestJS】ガチ入門【その３： Provider 編】

# はじめに

NestJS ガチ入門シリーズ 第3弾です。
前回までの記事は以下のとおりです。

- [【NestJS】ガチ入門【その１：NestJSの概要とプロジェクト作成・立ち上げ】](https://qiita.com/togswr/items/b901dfb4418780b81bbe)
- [【NestJS】ガチ入門【その２： Controller 編】](https://qiita.com/togswr/items/536f9ce434aa3d000534)

今回参照する公式ドキュメントは以下の Provider 編になります。

https://docs.nestjs.com/providers

今回もほぼ翻訳という形で進めていきます。

# Provider とは

Provider とは NestJS の核となる概念 (fundamental concept) です。
NestJS で作成する Service, Repository, Factory, Helper など様々なクラスをプロバイダーとして扱うことができます。

Provider は依存関係として注入されることが可能であること、というのが Provider の主要な考えになります。
- オブジェクト同士は、互いに様々な関係を持つことができる
- これらのオブジェクトを「接続する」 ("wiring up") 機能は主に NestJS に委譲できる

前回の章（ Controller 編）では、シンプルな `CatsController` を作成しました。
コントローラは HTTP リクエストを扱うべきであり、さらに複雑な処理は Provider に委譲すべきです。
Privider は module 内で `providers` として宣言される生の　(plain)　JavaScript クラスです。

:::note info
NestJS によりオブジェクト指向な方法で依存関係の設計・整理できるようになるため、 [SOLID](https://en.wikipedia.org/wiki/SOLID) 原則に従うことを強くおすすめします。
:::

# Services

`CatsService` を作るところから始めましょう。
この Service はデータの保存・取得の責務を担います。
そして、 `CatsController` から使われるよう設計されているため、 Provider として定義される良い例です。

```ts:cats.service.ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}
```

`CatsService` は 1 つのプロパティと 2 つのメソッドを持つ、基本的なクラスです。
唯一の新しい特徴としては、 `@Injectable()` デコレータを使っていることです。

`@Injectable()` デコレータは、 `CatsService` が Nest の IoC コンテナで管理可能なクラスであることを宣言するメタデータを付与します。
ちなみに、この例では `Cats` インターフェイスを使用しており、これは以下のようになります。

```ts:interfaces/cat.interface.ts
export interface Cat {
  name: string;
  age: number;
  breed: string;
}
```

そして、この Service クラスを使用する `CatsController` は以下のようなものになります。

```ts:cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}
```

`CatsService` はクラスコンストラクターを介して注入 (inject) されます。
ここで、 `private` 構文を使っていることに注意してください。
この短い書き方によって、 `catsService` メンバの宣言および初期化を即座に同じ場所で行うことができます。

# Dependency Injection

Nest は Dependency Injection として知られる、強力な設計パターンを中心に構築されています。
これについては [Angular](https://angular.dev/guide/di) の公式ドキュメントを読むことをおすすめします。

Nest では、 TypeScript　の能力によって、依存関係が型によって解決されるため、非常に簡単に依存関係を管理することができます。
以下の例では、 Nest は `CatsService` のインスタンスを作成し返すことで、 `catsService` を解決します。
（もしくはシングルトンの一般的なケースでは、他の場所ですでにリクエストされていた場合、既存のインスタンスを返します。）

```ts
constructor(private catsService: CatsService) {}
```

# Scopes

Provider は基本的にアプリケーションのライフサイクルと同期されたライフタイム（ "scope" ）を持ちます。
アプリケーションが立ち上がる（ bootstrap )とき、すべての依存関係が解決されている必要があり、それにともなってすべての Provider がインストールされる。
同様に、アプリケーションが終了するとき各 Provider は破棄される。

しかし、 Provider のライフタイムをリクエスト単位（**request-scoped**）にすることもできます。
これらの詳細な手法は[こちら](https://docs.nestjs.com/fundamentals/injection-scopes)をご参照ください。

# Custom Providers

NestJS は Provider 間の関係を解決する IoC (inversion of control: 制御の反転） コンテナが組み込まれています。
これは上記で触れた依存関係の注入の基礎になりますが、実際にはこれまで触れてきた内容よりもっとパワフルなものです。

生 (plain) の値、クラス、非同期的もしくは同期的なファクトリーなどを使って Provider を定義することができます。
より多くの例は[こちら](https://docs.nestjs.com/fundamentals/custom-providers)からご確認ください。

# Optional Providers

解決する必要のない依存を持つプロバイダーの場合、 `@Optional()` デコレータをコンストラクタのシグネチャで指定する。
例えば、そのクラスが設定オブジェクト （Configuration Object) に依存しているけれど、それが渡されなかった場合、デフォルト値を使えばよい場合など。

```typescript
import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
```

上記の例では、  Custom Provider を使用していることに注意してください。
これは `HTTP_OPTIONS` という Custom **Token** を含めているためです。
以前の例では、コンストラクタベースの依存性注入を見てきました。
Custom Provider とそれに紐づけられる Token については[こちら](https://docs.nestjs.com/fundamentals/custom-providers)をご覧ください。

# Property-based injection

ここまではコンストラクタメソッドにプロバイダが注入される constructor-based injection を見てきました。
ごくまれに、 property-based injection が役立ちます。
例えば、上位クラスが1つ以上のプロバイダーに依存している場合、すべてのサブクラスのコンストラクタから `super()` メソッドを呼び出していくのはめんどくさいです。
これを避けるために、 `@Inject()` デコレータをプロパティレベルで呼ぶことが可能です。

```typescript
import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  @Inject('HTTP_OPTIONS')
  private readonly httpClient: T;
}
```

:::note warn
もしクラスが他のクラスを継承（extend）していない場合、基本的に constructor-based injection を使用すべきです。
:::

# Provider registration

これまでに `CatsService` を定義し、さらにそのサービスを使用する `CatchController` があります。
そして、サービスを使用するためにサービスを登録する必要があり、それは注入を行うことで可能です。
これはモジュールファイル（ `app.module.ts` ）内の `@Module()` デコレータの `providers` 配列にサービスを追加することで行えます。

```typescript
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class AppModule {}
```

これによって Nest は `CatsController` クラスの依存関係を解決できるようになります。
このときのディレクトリ構造は以下のとおりです。

```
src
├ cats
│ ├ dto
│ │ └ create-cat.dto.ts
│ ├ interfaces
│ │ └ cat.interface.ts
│ ├ cats.controller.ts
│ └ cats.service.ts
├ app.module.ts
└ main.ts
```

# Manual instantiation

ここまで、 Nest がどのようにして自動的に依存関係を解決するのかの詳細を見てきました。
特定の状況では、備え付けの DI （依存関係注入）システムの外にて手動でプロバイダーを取得、もしくはインスタンス化する必要があります。
これは、以下の2種類の方法で行うことができます。

- [Module reference](https://docs.nestjs.com/fundamentals/module-ref): 動的に Provider のインスタンスを取得もしくは生成するときに使用できます
- [Standalone applications](https://docs.nestjs.com/standalone-applications): `bootstrap()` 内で Provider を取得したいときに使用できます
    - 例: Controller を使用しないスタンドアロンなアプリケーション、立ち上げ時に configuration service を使用する場合

# まとめ

今回もドキュメントをなぞるだけになってしまいましたが、個人的には問題解決ベースで読む際より知らないことが知れて良かったと思います。
特に、 Optional Provider なるものがあるのは知れて良かったです。