NestJS公式ドキュメント翻訳
原文
Providers | NestJS - A progressive Node.js web framework
プロバイダ
プロバイダはNestの基本概念です。基本的なNestクラスの多くはサービス、リポジトリ、ファクトリー、ヘルパーなどのプロバイダとして扱われます。プロバイダのアイデアは依存関係を注入できることです。これはオブジェクトが互いにさまざまな関係を作成できることを意味し、オブジェクトのインスタンスを「繋げる」機能はNestランタイムシステムに委任できます。プロバイダは単に@Injectable()
デコレータが付けられたクラスです。
前の章では簡単なCatsController
を作成しました。コントローラはHTTPリクエストを処理し、より複雑なタスクをプロバイダに委任する必要があります。プロバイダはクラス宣言の前に@Injectable()
デコレータが付いたプレーンなJavaScriptクラスです。
Nestを使用するとオブジェクト指向で依存関係を設計および整理できるため、SOLIDの原則に従うことを強くお勧めします。
サービス
まずは、簡単なCatsService
を作成してみましょう。このサービスはデータの保存と取得を担当し、CatsController
で使用されるように設計されているためプロバイダとして定義するのに適しています。したがって、クラスを@Injectable()
で装飾します。
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;
}
}
CLIを使用してサービスを作成するには$nest g service cats
コマンドを実行します。
CatsService
は1つのプロパティと2つのメソッドを持つ基本的なクラスです。唯一の新しい機能は@Injectable()
デコレータを使用している点です。@Injectable()
デコレータは、このクラスがNestのプロバイダであることをNestに伝えるためのメタデータを付与します。ちなみに、この例では次のようなCat
インターフェースを使用しています。
export interface Cat {
name: string;
age: number;
breed: string;
}
catsを取得するサービスクラスができたので、CatsController
で使用しましょう。
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 readonly catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
CatsService
はコンストラクタを通して注入されます。private readonly
構文が使用されていることに注意してください。これによって、catsService
メンバーを宣言し同じ場所ですぐに初期化することができます。
依存性の注入
Nestは一般的に依存性注入と呼ばれる強力な設計パターンをもとに構築されています。Angularの公式ドキュメントにこの概念に関するわかりやすい記事があるので読むことをお勧めします。
NestではTypeScriptのおかげで依存関係は型ごとに解決されるため、非常に簡単に管理することができます。次の例では、NestはCatsService
のインスタンスを作成して返すことによりcatsService
を解決します(または一般的なシングルトンの場合、既に他の場所で使用されている場合は既存のインスタンスを返します)。この依存関係が解決され、コントローラのコンストラクタに渡されます(または指定されたプロパティに割り当てられます)。
constructor(private readonly catsService: CatsService) {}
スコープ
プロバイダは通常、アプリケーションのライフサイクルと同期した有効期間(「スコープ」)を持っています。アプリケーションがブートストラップされると、すべての依存関係を解決するためにすべてのプロバイダをインスタンス化する必要があります。同様に、アプリケーションが終了するとプロバイダは破棄されます。ただし、プロバイダの有効期間をリクエストスコープにする方法もあります。この手法の詳細についてはこちらをご覧ください。
カスタムプロバイダ
Nestにはプロバイダ間の関係を解決する組み込みの制御の反転(「IoC」)コンテナがあります。この機能は前述の依存性注入機能の基礎になっていますが、実際にはこれまで説明してきた機能よりもはるかに強力です。@Injectable()
デコレータは氷山の一角にすぎず、プロバイダを定義する唯一の方法ではありません。実際プレーンな値、クラス、および非同期または同期ファクトリーを使用できます。ここでさらに例を示します。
オプションのプロバイダ
場合によっては必ずしも解決する必要のない依存関係の場合あります。たとえば、クラスは構成オブジェクトに依存する場合がありますが、何も渡されない場合はデフォルト値を使用する必要があります。このような場合プロバイダがなくてもエラーにならないため、依存関係はオプションになります。
プロバイダがオプションであることを示すには、コンストラクタで@Optional()
デコレータを使用します。
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(
@Optional() @Inject('HTTP_OPTIONS') private readonly httpClient: T
) {}
}
上記の例ではカスタムプロバイダを使用していることに注意してください。これがHTTP_OPTIONS
カスタムトークンを含める理由です。前の例では、コンストラクタのクラスを介した依存関係を示すコンストラクタベースの注入を示しました。カスタムプロバイダとその関連トークンの詳細についてはこちらをご覧ください。
プロパティベースの注入
プロバイダはコンストラクタを介して注入されるため、これまで使用してきた手法はコンストラクタベースの注入と呼ばれます。非常に特殊なケースですがプロパティベースの注入が役立つ場合があります。たとえば、トップレベルのクラスが1つまたは複数のプロバイダに依存している場合、コンストラクタからサブクラスのsuper()
を呼び出してそれらを渡すことは非常に面倒です。これを回避するためにプロパティレベルで@Inject()
デコレータを使用できます。
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
ただし、クラスが別のプロバイダを拡張しない場合はコンストラクタベースのインジェクションを常に使用することをお勧めします。
プロバイダの登録
プロバイダ(CatsService
)を定義しそのサービスのコンシューマー(CatsController
)を取得したので、注入を実行できるようにNestにサービスを登録する必要があります。これを行うには、モジュールファイル(app.module.ts
)を編集し、@Module()
デコレータのproviders
配列にサービスを追加します。
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.service.ts
│ └── cats.controller.ts
├── app.module.ts
└── main.ts