0
votes

I'm currently working through the database integration docs for NestJS using TypeOrm. In these docs there are examples that show how to inject a custom database repository using the app.module from NestJS. All of these examples inject classes using the actual type of the custom repository.

@Injectable()
export class AuthorService {
  constructor(private authorRepository: AuthorRepository) {}
}

This code is injected via the app.modules by providing a import like such:

@Module({
  imports: [TypeOrmModule.forFeature([AuthorRepository])],
  controller: [AuthorController],
  providers: [AuthorService],
})
export class AuthorModule {}

This works well if you are fine with programming against an implementation, but I prefer to use an interface in my classes. I've already found the solution to injecting classes via an interface with NestJS in a previous question, but when I try to inject my custom repository like that, it doesn't seem to instanciate correctly and becomes undefined.

(node:16658) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'save' of undefined

Because of this, I assume you can only inject customRepositories via the forFeature() call in the app.module, but that won't allow me to use interfaces for injection, as far as I know. Is there any other way I can inject a custom TypeOrm repository without having the replace all my interfaces for the implementation of my custom repository? Thanks in advance!

Edit

Here is my current code, I managed to get it to inject, but this still forces me to use the implementation instead of the interface each time I call the constructor. This is mainly an issue when testing due to mocking.

  @CommandHandler(FooCommand)
export class FooHandler
  implements ICommandHandler<FooCommand> {

  private fooRepository: IFooRepository; // Using Interface as a private property.
  private barEventBus: IEventBus;
  
  constructor(fooRepository: FooRepository,
     barEventBus: EventBus) { // Forced to use implementation in constructor for injection.
    this.fooRepository = fooRepository;
    this.barEventBus = barEventBus;
  }
@EntityRepository(FooEntity)
export class FooRepository extends Repository<FooEntity> implements IFooRepository {

  getFoo() {
    // Do stuff
  }
}

@Module({
  imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([FooRepository]],

  // Other module setup
})
export class AppModule {}
1
Why would you like to inject the custom repositories as interfaces instead of classes?noam steiner
Because programming against the interface helps in keeping my code separated. The forFeature() injection forces me to use an implementation in my constructor, while I would like to keep that an interface.Jordi
@Jordi can you please include the code where you're trying to inject your custom repository so we can see the whole scenarioJesse Carter
I've added my code. I got it to work with a class implementation, but I would still prefer to use a interface annotation in my constructor class, if possible ofcourse.Jordi

1 Answers

1
votes

It should work with using the InjectRepository decorator where you specify the Repository but then you type is as your interface instead and when testing you just provide the IFooRepository!

Example code:

  constructor(@InjectRepository(FooRepository) fooRepository: IFooRepository,
 barEventBus: EventBus) {