Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NestJS - Cannot inject a service into a subscriber

I have a subscriber for NestJS to listen to any create, update or delete events (TypeORM). When one of these events is fired, I'd like to use an injected service in order to create a new revision entry.

However, it seems I cannot get the dependency loaded inside of the subscriber and the service comes back as being undefined

Key files:

  • EntityModificationSubscriber (Subscriber)
  • RevisionEntity (Entity)

app.module.ts

@Module({
  imports: [
      HttpModule,
      TypeOrmModule.forRoot({
          type: (process.env.DB_TYPE as any) || 'postgres',
          host: process.env.DB_HOST || '127.0.0.1',
          port: (process.env.DB_PORT as any) || 5432,
          username: process.env.DB_USER || 'root',
          password: process.env.DB_PASS || '',
          database: process.env.DB_NAME || 'test',
          entities: [join(__dirname, '**/**.entity{.ts,.js}')],
          synchronize: true,
          logging: 'all',
          logger: 'advanced-console',
          subscribers: [EntityModificationSubscriber],
      }),
      TypeOrmModule.forFeature([
          RevisionEntity,
      ]),
      TerminusModule.forRootAsync({
          // Inject the TypeOrmHealthIndicator provided by nestjs/terminus
          inject: [TypeOrmHealthIndicator, MicroserviceHealthIndicator],
          useFactory: (db, msg) => getTerminusOptions(db, msg),
      }),
      GraphQLModule.forRoot({
          debug: true,
          playground: true,
          typePaths: ['./**/*.graphql'],
      }),
  ],
  controllers: [AppController],
  providers: [
      RevisionService,
      EntityModificationSubscriber,
  ],
})

entity_modification_subscriber.ts

import {EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent} from 'typeorm';
import {RevisionEntity, RevisonEntityStatus} from '../entities/revison.entity';
import {RevisionService} from '../services/revisions.service';
import {Injectable} from '@nestjs/common';

@Injectable()
@EventSubscriber()
export class EntityModificationSubscriber implements EntitySubscriberInterface {

    constructor(private revisionService: RevisionService) {
    }

    // tslint:disable-next-line:no-empty
    afterInsert(event: InsertEvent<any>): Promise<any> | void {
        const revision = new RevisionEntity();

        revision.action = RevisonEntityStatus.Created;
    }

    afterUpdate(event: UpdateEvent<any>): Promise<any> | void {
    }

    // tslint:disable-next-line:no-empty
    afterRemove(event: RemoveEvent<any>) {
        // this.revisionService.createRevisionEntry(revision);
    }
}
like image 956
JPanda Avatar asked Nov 18 '19 16:11

JPanda


2 Answers

The only way I found to inject a dependency into a subscriber using NestJS, was not to register that subscriber in the TypeORM configuration. I subscribe it manually into the TypeORM connection on subscriber's constructor.

import { EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent, Connection } from 'typeorm';
    import { RevisionEntity, RevisonEntityStatus } from '../entities/revison.entity';
    import { RevisionService } from '../services/revisions.service';
    import { Injectable } from '@nestjs/common';

    @Injectable()
    @EventSubscriber()
    export class EntityModificationSubscriber implements EntitySubscriberInterface {

        constructor(private readonly connection: Connection, private readonly revisionService: RevisionService) {
            connection.subscribers.push(this); // <---- THIS 
        }

        // tslint:disable-next-line:no-empty
        afterInsert(event: InsertEvent<any>): Promise<any> | void {
            const revision = new RevisionEntity();

            revision.action = RevisonEntityStatus.Created;

            //this.revisionService <- should be working!
        }

        afterUpdate(event: UpdateEvent<any>): Promise<any> | void {
        }

        // tslint:disable-next-line:no-empty
        afterRemove(event: RemoveEvent<any>) {
            // this.revisionService.createRevisionEntry(revision);
        }
    }

Then in your app.module on the TypeORM module configuration (TypeOrmModule.forRoot). Remove the line:

subscribers: [EntityModificationSubscriber],

It solved to me, hope it help others. You can find discussions about that in some NestJS issues/pull requests.

https://github.com/nestjs/typeorm/issues/85

https://github.com/nestjs/typeorm/pull/27

like image 166
jplindgren Avatar answered Oct 23 '22 04:10

jplindgren


Hi the problem is that If you want to preform database actions you would have to use:

event.manager

and Don't use getEntityManager() or getRepository() or any other global function. This is due to a transaction. Save is running in a transaction and your data is saved in a transaction which is not committed yet. But global functions you use are running out of transaction.

more about issue here:

https://github.com/typeorm/typeorm/issues/681

like image 23
Loki Avatar answered Oct 23 '22 06:10

Loki