diff --git a/e2e/health-checks/mikro-orm.health.e2e-spec.ts b/e2e/health-checks/mikro-orm.health.e2e-spec.ts index 87da64a9c..9735721a7 100644 --- a/e2e/health-checks/mikro-orm.health.e2e-spec.ts +++ b/e2e/health-checks/mikro-orm.health.e2e-spec.ts @@ -1,3 +1,4 @@ +import { MikroORM } from '@mikro-orm/core'; import { type INestApplication } from '@nestjs/common'; import * as request from 'supertest'; import { @@ -76,6 +77,42 @@ describe('MikroOrmHealthIndicator', () => { details, }); }); + + it('should indicate that mikroOrm is down if the connection has been closed after startup', async () => { + app = await setHealthEndpoint(({ healthCheck, mikroOrm }) => + healthCheck.check([async () => mikroOrm.pingCheck('mikroOrm')]), + ).start(); + + const up = { + mikroOrm: { + status: 'up', + }, + }; + + request(app.getHttpServer()).get('/health').expect(200).expect({ + status: 'ok', + info: up, + error: {}, + details: up, + }); + + const orm = app.get(MikroORM); + await orm.close(); + + const down = { + mikroOrm: { + status: 'down', + message: 'Not connected to database', + }, + }; + + return request(app.getHttpServer()).get('/health').expect(503).expect({ + status: 'error', + info: {}, + error: down, + details: down, + }); + }); }); }); diff --git a/lib/errors/database-not-connected.error.ts b/lib/errors/database-not-connected.error.ts new file mode 100644 index 000000000..3fdba4a53 --- /dev/null +++ b/lib/errors/database-not-connected.error.ts @@ -0,0 +1,11 @@ +import { DATABASE_NOT_CONNECTED } from './messages.constant'; + +/** + * Error which gets thrown when the database is not connected + * @publicApi + */ +export class DatabaseNotConnectedError extends Error { + constructor() { + super(DATABASE_NOT_CONNECTED); + } +} diff --git a/lib/errors/messages.constant.ts b/lib/errors/messages.constant.ts index 39601f49e..e2402a703 100644 --- a/lib/errors/messages.constant.ts +++ b/lib/errors/messages.constant.ts @@ -8,7 +8,7 @@ export const CONNECTION_NOT_FOUND = * @internal */ export const TIMEOUT_EXCEEDED = (timeout: number) => - `timeout of ${timeout.toString()}ms exceeded`; + `Timeout of ${timeout.toString()}ms exceeded`; /** * @internal @@ -21,3 +21,8 @@ export const STORAGE_EXCEEDED = (keyword: string) => */ export const UNHEALTHY_RESPONSE_CODE = (responseCode: string | number) => `The service returned an unhealthy response code: ${responseCode}`; + +/** + * @internal + */ +export const DATABASE_NOT_CONNECTED = `Not connected to database`; diff --git a/lib/health-indicator/database/mikro-orm.health.ts b/lib/health-indicator/database/mikro-orm.health.ts index 40a6a8624..f413659ff 100644 --- a/lib/health-indicator/database/mikro-orm.health.ts +++ b/lib/health-indicator/database/mikro-orm.health.ts @@ -3,7 +3,8 @@ import { Injectable, Scope } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { HealthIndicator, type HealthIndicatorResult } from '..'; import { TimeoutError } from '../../errors'; -import { HealthCheckError } from '../../health-check'; +import { DatabaseNotConnectedError } from '../../errors/database-not-connected.error'; +import { HealthCheckError } from '../../health-check/health-check.error'; import { TimeoutError as PromiseTimeoutError, promiseTimeout, @@ -74,9 +75,7 @@ export class MikroOrmHealthIndicator extends HealthIndicator { }), ); } - - // Check if the error is a connection not found error - if (error instanceof Error) { + if (error instanceof DatabaseNotConnectedError) { throw new HealthCheckError( error.message, this.getStatus(key, false, { @@ -84,6 +83,11 @@ export class MikroOrmHealthIndicator extends HealthIndicator { }), ); } + + throw new HealthCheckError( + `${key} is not available`, + this.getStatus(key, false), + ); } return this.getStatus(key, true); @@ -120,7 +124,13 @@ export class MikroOrmHealthIndicator extends HealthIndicator { * */ private async pingDb(connection: MikroOrm.Connection, timeout: number) { - const isConnected = connection.isConnected(); - return await promiseTimeout(timeout, isConnected); + const checker = async () => { + const isConnected = await connection.isConnected(); + if (!isConnected) { + throw new DatabaseNotConnectedError(); + } + }; + + return await promiseTimeout(timeout, checker()); } }