Search code examples
nestjsbackendnestjs-config

NestJS lifecycle methods invoked without implementing their interface


I am having a small question about NestJS. In my code, there is a service which looks something like:

`

import { Inject, Injectable } from '@nestjs/common';

import neo4j, { Driver, int, Result, Transaction } from 'neo4j-driver';

import { Neo4jConfig } from './neo4j-config.interface';
import { NEO4J_CONFIG, NEO4J_DRIVER } from './neo4j.constants';

@Injectable()
export class Neo4jService {
  constructor(
    @Inject(NEO4J_CONFIG) private readonly config: Neo4jConfig,
    @Inject(NEO4J_DRIVER) private readonly driver: Driver,
  ) {}

  onApplicationBootstrap() {
    console.log('Hello');
  }

  getDriver(): Driver {
    return this.driver;
  }

  getConfig(): Neo4jConfig {
    return this.config;
  }

  int(value: number) {
    return int(value);
  }

  beginTransaction(database?: string): Transaction {
    const session = this.getWriteSession(database);

    return session.beginTransaction();
  }

  getReadSession(database?: string) {
    return this.driver.session({
      database: database || this.config.database,
      defaultAccessMode: neo4j.session.READ,
    });
  }

  getWriteSession(database?: string) {
    return this.driver.session({
      database: database || this.config.database,
      defaultAccessMode: neo4j.session.WRITE,
    });
  }

  read(
    cypher: string,
    params?: Record<string, unknown>,
    databaseOrTransaction?: string | Transaction,
  ): Result {
    if (databaseOrTransaction instanceof Transaction) {
      return (<Transaction>databaseOrTransaction).run(cypher, params);
    }

    const session = this.getReadSession(<string>databaseOrTransaction);
    return session.run(cypher, params);
  }

  write(
    cypher: string,
    params?: Record<string, unknown>,
    databaseOrTransaction?: string | Transaction,
  ): Result {
    if (databaseOrTransaction instanceof Transaction) {
      return (<Transaction>databaseOrTransaction).run(cypher, params);
    }

    const session = this.getWriteSession(<string>databaseOrTransaction);
    return session.run(cypher, params);
  }

  private onApplicationShutdown() {
    console.log('Goodbye')
    return this.driver.close();
  }
}

` Then in my main.ts file I have this method called:

`

  await app.listen(port);

`

As you can see my service does not implement neither onApplicationBootstrap nor onApplicationShutdown. How does it come that those methods still get invoked? Should I implement onApplicationBootstrap and onApplicationShutdown or not? As you can also see I' d like that my onApplicationBootstrap is a private method which would not be possible if I implement the interface. So, I would like to ask you:

  • Why the two lifecycle methods get called event without implementing the interface?
  • Should I implement those interfaces at all or just go on and use the methods which would allow me to define them as private?

I expected those methods to not work without implementing the interfaces


Solution

  • The Typescript interface is there to help us as devs. It doesn't exist at runtime, there's no information about it, so the only thing Nest can do is just check "Hey, does this class have the onModuleInit method?" If yes, add it to a list of classes to call onModuleInit. Do the same with the other lifecycle methods.

    The interfaces aren't explicitly necessary, but they do give us devs a better idea of the class by just looking at the export class... line because we can see what is implemented/extended.