nestjs / terminus

Terminus module for Nest framework (node.js) :robot:

Home Page:https://nestjs.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MikroOrmHealthIndicator does not work with the `defineConfig` helper

ctfdavis opened this issue · comments

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Summary of the issue

Currently, the pingDb method of the MikroOrmHealthIndicator class makes use of the type option of the MikroOrm configuration. However, the type would be undefined if the defineConfig helper exported from the driver package is used for the MikroOrm configuration without providing the type option, causing the pingDb method to fail with a rather cryptic error message:

undefined ping check is not implemented yet

In addition, the type option will be removed in MikroOrm v6, and the suggested way of defining configuration is using the defineConfig helper exported from the driver package.

The issue

Here’s the current implementation of pingDb method, which would throw a cryptic error message undefined ping check is not implemented yet if the user uses the defineConfig helper exported from the driver package to configure MikroOrm without providing the type option:

  private async pingDb(connection: MikroOrm.Connection, timeout: number) {
    let check: Promise<any>;
    const type = connection.getPlatform().getConfig().get('type’); // type can be undefined here!

    switch (type) {
      case 'postgresql':
      case 'mysql':
      case 'mariadb':
      case 'sqlite':
        check = connection.execute('SELECT 1');
        break;
      case 'mongo':
        check = connection.isConnected();
        break;
      default:
        throw new NotImplementedException(
          `${type} ping check is not implemented yet`, // if type is undefined, then we’d get a cryptic error message
        );
    }
    return await promiseTimeout(timeout, check);
  }

One way to fix it is to check the driver instead, which would work with both the original way of configuration and defineConfig helper:

  private async pingDb(connection: MikroOrm.Connection, timeout: number) {
    let check: Promise<any>;
    const driver = connection.getPlatform().getConfig().getDriver().constructor.name;

    switch (driver) {
      case 'PostgreSqlDriver':
      case 'MySqlDriver':
      case 'MariaDbDriver':
      case 'SqliteDriver':
        check = connection.execute('SELECT 1');
        break;
      case 'MongoDriver':
        check = connection.isConnected();
        break;
      default:
        throw new NotImplementedException(
          `${driver} ping check is not implemented yet`,
        );
    }

    return await promiseTimeout(timeout, check);
  }

Unlike get(‘type’) which may return undefined, the getDriver() method always returns a database driver (of the IDatabaseDriver interface), so the above switch case would no longer have an undefined case. Therefore, even in the default case, the user would no longer encounter a cryptic error message, i.e., undefined ping check is not implemented yet.

Let me know if the above proposal is good enough. I am happy to help create a pull request to fix this issue.

Thanks everyone!

Minimum reproduction code

In the sample 011-mirkoorm-app, make the following changes in health.module.ts to use the defineConfig helper from MikroOrm:

import { MikroOrmModule } from "@mikro-orm/nestjs";
import { Module } from "@nestjs/common";
import { HealthController } from "./health.controller";
import { TerminusModule } from "@nestjs/terminus";
import { defineConfig } from "@mikro-orm/mysql";

@Module({
  imports: [
    // MikroOrmModule.forRoot({
    //   type: 'mysql',
    //   dbName: 'test',
    //   user: 'root',
    //   password: 'root',
    //   host: '0.0.0.0',
    //   port: 3306,
    //   discovery: { warnWhenNoEntities: false }, // disable validation entities
    //   strict: true,
    // }),
    MikroOrmModule.forRoot(
      defineConfig({
        dbName: "test",
        user: "root",
        password: "root",
        host: "0.0.0.0",
        port: 3306,
        discovery: { warnWhenNoEntities: false }, // disable validation entities
        strict: true,
      })
    ),
    TerminusModule,
  ],
  controllers: [HealthController],
})
export class HealthModule {}

Run the app and make a GET request to the /health endpoint. The below cryptic error message would be returned:

undefined ping check is not implemented yet

Steps to reproduce

No response

Expected behavior

  • Should not throw a cryptic error message such as undefined ping check is not implemented yet
  • Should be able to work with the defineConfig helper

Package version

9.2.0

NestJS version

9.3.2

Node.js version

No response

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

@ctfdavis Awesome investigation! Appreciate it! Would you like to create a PR?

It would also be good to know when connection.getPlatform().getConfig().getDriver() was introduced. If that is a rather newer-ish way to access the driver type we might need to use a fallback to the legacy connection.getPlatform().getConfig().get('type’)

@BrunnerLivio Upon further investigation, I think the differentiation between sql and nosql drivers in the current pingDb method is actually unnecessary.

The Connection abstract class from MikroORM has an abstract method isConnected() (source) intended for checking if the connection to the database has been successfully established. isConnected() has been a required method for the Connection class since MikroORM v2 (4 years ago, source). So it is definitely safe to use it in this library.

All built-in connection classes extend the Connection abstract class. Specifically, the MongoConnection directly extends the abstract class and implements it the mongo way (source). For all sql client connections, including the MySqlConnection class, they extend the AbstractSqlConnection class (source) which extends the Connection class and implements the isConnected() method using a sql query (essentially the same as what the current pingDb method does, source).

So, with the above information, I think we can simplify the pingDb method as below:

  private async pingDb(connection: MikroOrm.Connection, timeout: number) {
    return await promiseTimeout(timeout, connection.isConnected());
  }

Not only is it simpler and works with all built-in connection types since MikroORM v2, it has two additional benefit:

  • it is future-proof to added built-in connection types
  • it automatically supports custom connection types (see here, custom connections are required to extend the Connection class)

Let me know if this proposal is good. I will create a PR afterwards if you agree. Thanks a lot!

@ctfdavis Sounds good to me!

Released with v10.1.0 🎉