Search code examples
node.jsmongodbmongoosenestjsmongoose-sequence

Auto Increment Sequence in NestJs/Mongoose


I'm migrating a NodeJs project to NestJs, this project uses MongoDB as back-end database and Mongoose as ODM. I was using the mongoose-sequence plugin to handle autoincrement sequences, however I'm facing troubles requiring the library under NestJs.

The mongoose-sequence documentation explains how to import the library using CommonJS syntax as follows:

const mongoose = require('mongoose')
const AutoIncrementFactory = require('mongoose-sequence');

const connection = await mongoose.createConnection('mongodb://...');

const AutoIncrement = AutoIncrementFactory(connection);

Using ES6 import syntax it would be something like:

import * as mongoose from 'mongoose';
import * as AutoIncrementFactory from 'mongoose-sequence';

const connection = ...;

const AutoIncrement = AutoIncrementFactory(connection);

However since NestJs uses Dependency Injection, accessing the native connection is not so direct. According to the documentation to integrate MongoDB using Mongoose accessing the native Mongoose Connection object can be done using the @InjectConnection() decorator as follows:

@Injectable()
export class CatsService {
  constructor(@InjectConnection() private connection: Connection) {}
}

But since TypeScript decorators can only be attached to a class declaration, method, accessor, property, or parameter I don't see how to inject the connection, require the plugin and initialize it on my Schema classes.


Solution

  • It's possible to register a plugin for a given schema using the forFeatureAsync() method of the MongooseModule along with a factory provider (i.e., useFactory).

    Following the example from the official documentation:

    @Module({
      imports: [
        MongooseModule.forFeatureAsync([
          {
            name: Cat.name,
            useFactory: () => {
              const schema = CatsSchema;
              schema.plugin(require('mongoose-autopopulate'));
              return schema;
            },
          },
        ]),
      ],
    })
    export class AppModule {}
    

    However with the mongoose-sequence plugin it's necessary to pass the native Mongoose connection object to the plugin initialization. This can be achieved by injecting the connection into the factory provider with the getConnectionToken method:

    import {getConnectionToken, MongooseModule} from '@nestjs/mongoose';
    import * as AutoIncrementFactory from 'mongoose-sequence';
    
    @Module({
      imports: [
        MongooseModule.forFeatureAsync([
          {
            name: Cat.name,
            useFactory: async (connection: Connection) => {
              const schema = CatsSchema;
              const AutoIncrement = AutoIncrementFactory(connection);
              schema.plugin(AutoIncrement, {inc_field: 'id'});
              return schema;
            },
            inject: [getConnectionToken('YOUR_CONNECTION_NAME')],
          },
        ]),
      ],
    })
    export class AppModule {}