Provider

プロバイダ(サービス、リポジトリ、ファクトリ、ヘルパーなど)はコントローラのコンストラクタを介して依存関係を注入することができ、相互にさまざまな関係を作成することが出来ます。 しかし、プロバイダは@Injectableデコレータでインジェクションするだけの単純なクラスだけではありません。プロバイダはビジネスロジックを担当し、コントローラーや他のプロバイダから呼び出されます。

f:id:adrenaline2017:20191130163859p:plain

コントローラはHTTPリクエストを処理し、より複雑なタスクをサービスに任せるものでした。 @Injectableデコレータを持つクラスをNestではプロバイダと認識します。

サービス

サービスの集まりがプロバイダです。単純なCatsServiceプロバイダを作成することから始めましょう。

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デコレータにはメタデータが含まれているので、NestはこのクラスがNestプロバイダであると認識します。 Catインターフェイスを使用していることに注意してください。 すでにサービスクラスが用意されているので、CatsControllerの内部でそれを使用しましょう:

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 readonly catsService: CatsService) {}

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

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

CatsServiceは、コントーローラのクラスのコンストラクタを通して注入されます。

依存性注入(DI:Dependency Injection)

オブジェクト指向においてオブジェクト同士が複雑に干渉し合うことは問題視されます。そこでDIコンテナーと言う仕組み(オブジェクト同士の依存関係を橋渡しする為の仕組み)を利用します。DIとは依存性(Dependency)を外から注入(Injection)すると言う意味です。

NestJSでは、TypeScriptの機能のおかげで依存関係をタイプごとに解決しています。そしてこれにより、コントローラのコンストラクタに渡し注入することができるため、依存関係を管理するのは非常に簡単です。コンストラクターの引数型と、登録すみのサービスとを照合して注入すべきオブジェクトを決定する決まりがあります。

@Controller('cats')
export class CatsController {
  //ここで注入
  constructor(private readonly catsService: CatsService) {}

オプションプロバイダ

場合によっては、必ずしも解決する必要のない場合があります。 それは、クラスが構成オブジェクトに依存する場合です。何も渡されなかった場合は、デフォルト値を使用する必要があります。 このような場合、構成するプロバイダが不足してもエラーにならない様にするため、コンストラクシグネチャに@Optional()デコレータを使用します。

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

@Injectable()
export class HttpService {
  constructor(
    @Optional() @Inject('HTTP_OPTIONS') private readonly httpClient,
  ) {}
}

スコープ

CatsServiceというものが存在することをモジュールに伝える必要があります。 これを行うには、モジュールファイルapp.module.tsを編集し、サービスを@Module()デコレータのproviders配列に内容を配置します。

app.module.ts

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

@Module({
  controllers: [CatsController],
  //ここにサービスの内容を記載
  providers: [CatsService],
})
export class ApplicationModule {}

これにより、NestはCatsControllerクラスの依存関係を解決できます。