How to close database connection in nestjs service?

Viewed 3272

In my database module I'm creating a connection to my database. With this setup I do get the error

A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --runInBand --detectOpenHandles to find leaks.

in my e2e tests.

I think I have to close the connection. But how do I run client.close in the onModuleDestroy() in my Databasemodule?

database.module.ts

import { Module } from '@nestjs/common'
import { MongoClient, Db, Logger } from 'mongodb'

@Module({
    providers: [
        {
            provide: 'DATABASE_CONNECTION',
            useFactory: async (): Promise<Db> => {
                const mongo = 'mongodb://localhost:27017'
                const database = 'testing'

                try {
                    Logger.setLevel('debug')

                    const client = await MongoClient.connect(mongo, {
                        useNewUrlParser: true,
                        useUnifiedTopology: true,
                    });

                    const db = client.db(database)
                    return db
                } catch (error) {
                    throw error
                }
            }
        },
        {
            provide: 'DATABASE_CLIENT',
            useFactory: () => true // how do I get client of the above provider?
        }
    ],
    exports: ['DATABASE_CONNECTION', 'DATABASE_CLIENT']
})

export class DatabaseModule {
    constructor(
        @Inject('DATABASE_CLIENT')
        private client: Db
    ) {}

    async onModuleDestroy() {
        console.log(this.client);
        // await this.client.close()
    }
}
1 Answers

Opened database connection is a common cause for this problem. A module needs to clean up after itself, this especially applies to testing where a module can be instantiated multiple times.

client reference needs to be preserved for a cleanup. This could be done with single class service:

@Injectable()
class DatabaseConnection {
  async onModuleInit() {
    // ...same as DATABASE_CONNECTION factory
    this.client = client;
    this.db = client.db(database);
  }
  async onModuleDestroy() {
    await this.client.close()
  } 
}

Or with two factory providers, one of which keeps a reference to client:

@Module({
  providers: [
    {
      provide: 'DATABASE_CLIENT',
      useFactory: () => ({ client: null })
    },
    {
      provide: 'DATABASE_CONNECTION',
      inject: ['DATABASE_CLIENT'],
      useFactory: async (dbClient) => {
        ...
        dbClient.client = client
        const db = client.db(database)
        return db;
      }
    }
  ]
  ...
})
export class DatabaseModule {
  constructor(@Inject('DATABASE_CLIENT') private dbClient) {}

  async onModuleDestroy() {
    await this.dbClient.client.close()
  } 
}
Related