【NestJS】ServiceクラスにServiceクラスを依存性注入(DI)する方法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573011/acf5c95c-3214-8daf-5963-201b37d492ed.png)

# はじめに
最近、実務で[NestJS](https://nestjs.com)を書き始めました！
NestJSに関する日本語の記事がまだまだ少ないので、自分用メモも兼ねてどんどん記事を書いていきたいです。

# 使用技術
- NestJS(TypeScript)
- GraphQL
- TypeORM

# クラス図
![class1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573011/a3d5151a-575f-4fba-4964-60f7bbdc4f16.png)

`HogeService`に`FugaService`を注入して、`HogeModule`を外部から利用したいというユースケースです。`HogeResolver`に`FugaService`を利用します。

# Serviceの実装
```ts:fuga.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import FugaEntity from 'src/entities/fuga';

@Injectable()
export class FugaService {
  constructor(
    @InjectRepository(FugaEntity)
    private fugaRepository: Repository<FugaEntity>, // Fugaエンティティを注入
  ) {}

  async find(id: number): Promise<FugaEntity> {
    return this.fugaRepository.findOne(id);
  }
}
```

```ts:hoge.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import HogeEntity from 'src/entities/hoge';
import { FugaService } from './fuga.service';

@Injectable()
export class HogeService {
  constructor(
    @InjectRepository(HogeEntity)
    private hogeRepository: Repository<HogeEntity>,  // Hogeエンティティを注入
    private fugaService: FugaService, // Serviceを注入
  ) {}

  async find(id: number): Promise<HogeEntity> {
    const fuga = await this.fugaService.find(1); // 注入したインスタンスを利用可能に！
    return this.hogeRepository.findOne(id);
  }
}
```

# 動かしてみると...
```
[Nest] 877  - 11/28/2021, 2:45:15 AM   ERROR [ExceptionHandler] Nest can't resolve dependencies of the FugaService (?). Please make sure that the argument FugaRepository at index [0] is available in the HogeModule context.

Potential solutions:
- If FugaRepository is a provider, is it part of the current HogeModule?
- If FugaRepository is exported from a separate @Module, is that module imported within HogeModule?
  @Module({
    imports: [ /* the Module containing FugaRepository */ ]
  })
```

一見、これだけで動きそうなものなんですが、NestJSはmoduleで依存関係を定義してあげないとエラーが出ます。

# moduleの実装
```ts:fuga.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FugaService } from 'src/services/fuga.service';
import FugaEntity from 'src/entities/fuga';

@Module({
  imports: [TypeOrmModule.forFeature([FugaEntity])], // エンティティを明記
  exports: [FugaModule, TypeOrmModule],
})
export class FugaModule {}
```

```ts:hoge.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HogeService } from 'src/services/hoge.service';
import HogeEntity from 'src/entities/hoge';
import { FugaModule } from './fuga.module';
import { FugaService } from 'src/services/fuga.service';
import { HogeResolver } from 'src/resolvers/hoge.resolver';

@Module({
  imports: [
    TypeOrmModule.forFeature([HogeEntity]),  // エンティティを明記
    FugaModule, // !!! 利用するモジュールを追記 !!!
  ],
  providers: [
    HogeService, 
    HogeResolver, 
    FugaService, // !!! 利用するサービスを追記 !!!
  ],
  exports: [HogeModule, TypeOrmModule],
})
export class HogeModule {}
```

あとは、使う側にmoduleを読み込んであればOK！

```ts:app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ApiModule } from './api.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
    }),
    TypeOrmModule.forRoot(),
    HogeModule, // !!! モジュールを読み込む !!!
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
```


# Resolverの実装
```ts:hoge.resolver.ts
import { Args, Int, Query, Resolver } from '@nestjs/graphql';
import Hoge from 'src/entities/hoge';
import { HogeService } from 'src/services/hoge.service';

@Resolver(() => Hoge)
export class HogeResolver {
  constructor(private hogeService: HogeService) {}

  @Query(() => Hoge)
  async getHoge(@Args({ type: () => Int }) id: number) {
    return this.hogeService.find(id);
  }
}
```
コンストラクタで`HogeService`を注入して、Resolverを実装することができます。ここには、`Fugaservice`は出てきません！


# 終わりに
コードをテスタブルにするために、ちゃんとService層を依存性注入(DI)するようにしたいですよね。NestJSはこれが簡単にできる仕組みを備えているので、ドキュメントをちゃんと読んで使いこなせるようになりたいです！
moduleに書かないといけないことによるメリットは、正直よくわかっていないです笑。

# appendix
```json:package.json
"dependencies": {
  "@nestjs/common": "^8.0.0",
  "@nestjs/core": "^8.0.0",
  "@nestjs/graphql": "^9.0.4",
  "@nestjs/platform-express": "^8.0.0",
  "@nestjs/typeorm": "^8.0.2",
  "apollo-server-express": "^3.3.0",
  "class-transformer": "^0.4.0",
  "class-validator": "^0.13.1",
  "graphql": "^15.5.3",
  "graphql-tools": "^8.2.0",
  "pg": "^8.7.1",
  "reflect-metadata": "^0.1.13",
  "rimraf": "^3.0.2",
  "rxjs": "^7.2.0",
  "typeorm": "^0.2.37"
},
"devDependencies": {
  "@nestjs/cli": "^8.0.0",
  "@nestjs/schematics": "^8.0.0",
  "@nestjs/testing": "^8.0.0",
  "@types/express": "^4.17.13",
  "@types/jest": "^27.0.1",
  "@types/node": "^16.0.0",
  "@types/supertest": "^2.0.11",
  "@typescript-eslint/eslint-plugin": "^4.28.2",
  "@typescript-eslint/parser": "^4.28.2",
  "eslint": "^7.30.0",
  "eslint-config-prettier": "^8.3.0",
  "eslint-plugin-prettier": "^3.4.0",
  "jest": "^27.0.6",
  "prettier": "^2.3.2",
  "supertest": "^6.1.3",
  "ts-jest": "^27.0.3",
  "ts-loader": "^9.2.3",
  "ts-node": "^10.0.0",
  "tsconfig-paths": "^3.10.1",
  "typescript": "^4.3.5"
},
```