Search code examples
dependency-injectioninterfacenestjsdependency-inversion

Nest.js Dependency Inversion function not found


I followed the controller-service-repository architecture and I want to use dependency inversion on StoneRepository. Having the code from bellow I get:

[Nest] 22656  - 03/21/2022, 5:01:44 PM   ERROR [ExceptionsHandler] this.stoneRepository.getStones is not a function

What have I done wrong? Please help.

constants.ts

export const STONE_REPOSITORY_TOKEN = Symbol("STONE_REPOSITORY_TOKEN");

app.module.ts

import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { StoneModule } from "./stone/stone.module";
  
@Module({
  imports: [StoneModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

stone.module.ts

import { Module } from "@nestjs/common";
import { StoneController } from "./stone.controller";
import { StoneService } from "./stone.service";
import { StoneRepository } from "./stone.repository";
import { STONE_REPOSITORY_TOKEN } from "./constants";

@Module({
  imports: [],
  controllers: [StoneController],
  providers: [
    {
      provide: STONE_REPOSITORY_TOKEN,
      useValue: StoneRepository,
    },
    StoneService,
  ],
})
export class StoneModule {}

stone.controller.ts

import { Controller, Get } from "@nestjs/common";
import { StoneService } from "./stone.service";
import { Stone } from "./domain/Stone";

@Controller()
export class StoneController {
  constructor(private stoneService: StoneService) {}

  @Get("/stone")
  async getStones(): Promise<Stone[]> {
    return await this.stoneService.getStones();
  }
}

stone.interface.repository.ts

import { Stone } from "./domain/Stone";

export interface StoneInterfaceRepository {
  getStones(): Promise<Stone[]>;
}

stone.service.ts

import { Inject, Injectable } from "@nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
import { STONE_REPOSITORY_TOKEN } from "./constants";

@Injectable()
export class StoneService {
  constructor(
    @Inject(STONE_REPOSITORY_TOKEN)
    private stoneRepository: StoneInterfaceRepository,
  ) {}
  async getStones(): Promise<Stone[]> {
    return await this.stoneRepository.getStones();
  }
}

stone.repository.ts

import { Injectable } from "@nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";

@Injectable()
export class StoneRepository implements StoneInterfaceRepository {
  async getStones(): Promise<Stone[]> {
    return Promise.resolve([new Stone()]);
  }
}

Solution

  • You are using useValue for the STONE_REPOSITORY_TOKEN token's custom provider. This means that Nest will inject the direct reference, not the class instance, so you have no access to instance methods, like getStones(). Change your module to this:

    @Module({
      imports: [],
      controllers: [StoneController],
      providers: [
        {
          provide: STONE_REPOSITORY_TOKEN,
          useClass: StoneRepository,
        },
        StoneService,
      ],
    })
    export class StoneModule {}