6
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-11-28

image.png

はじめに

最近、実務でNestJSを書き始めました!
NestJSに関する日本語の記事がまだまだ少ないので、自分用メモも兼ねてどんどん記事を書いていきたいです。

使用技術

  • NestJS(TypeScript)
  • GraphQL
  • TypeORM

クラス図

class1.png

HogeServiceFugaServiceを注入して、HogeModuleを外部から利用したいというユースケースです。HogeResolverFugaServiceを利用します。

Serviceの実装

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);
  }
}
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の実装

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 {}
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!

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の実装

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

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"
},
6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1