NestJS x TypeORMでcustom repositoryをDIしたかった

注) やりたいことについて、ただ失敗しただけなので有用性は有りません

これはなに

NestJSのORMとしてTypeORMを使っていた際、かゆいところに手が届かなかったので、これのメモ

背景

TypeORMは自動生成したRepositoryをDI(注入)し、saveupsertメソッドを独自実装することなく使用できる。

@Module({
  imports: [TypeOrmModule.forFeature([User])], // Dynamic module
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User) // users.module.tsでprovidersに登録されたrepositoryをDI
    private usersRepository: Repository<User>,
  ) {}

  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }
  ...

しかしこの方法だとDB操作に関する処理を独自に追加したくなったとき、サービスクラス(ロジック)にDBに関する処理が流出してしまう。 また単体テストを書く際に、Repositoryをモックしづらく、テストコードの整備が進まない。

そこでStack overflowを参考にTypeORMのRepository<T>を継承したクラスを追加

export class ArticlesRepository extends Repository<Article> {
  async customSave(dto: CreateArticleDto): Promise<Article> {
    console.log('custom save called!!');
    return await this.save(dto);
  }
}
@Module({
  // entityでなくrepositoryを引数に渡す
  // https://stackoverflow.com/questions/60777204/typeerror-repository-method-is-not-a-function-nestjs-typeorm
  imports: [TypeOrmModule.forFeature([ArticleRepository])],
  controllers: [ArticleController],
  providers: [ArticleService],
})
export class ArticlesModule {}

実行、、、失敗

[Nest] 74  - 07/26/2022, 8:55:33 AM   ERROR [ExceptionsHandler] this.repository.customSave is not a function
TypeError: this.repository.customSave is not a function
    at ArticleService.create (/backend/src/articles/article.service.ts:13:28)
    ...

調査するとこんなIssue

カスタムリポジトリのインスタンスは @nestjs/core ではなく typeorm ライブラリによって生成されるため、依存性注入機構を利用することはできません。それでも、EntityManager (typeorm からカスタムリポジトリに渡される) を使って、いつでも別のカスタムリポジトリのインスタンスを取得することができます。

なんだと、、、

EntityManagerをDI(コンストラクタインジェクション)することはできんのか、、?

PS) Laravelでクリーンアーキテクチャやるときみたいにロジックはインターフェースを使うよう(抽象に依存)にしたらいいのかも

まとめ

  • よく言われる「NodeのまともなORM問題」の一端に触れた気がする
  • 今やるならPrismaなのか???
  • 指摘歓迎