6

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.

Andres Felipe
  • 625
  • 7
  • 24

3 Answers3

8

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 {}
Andres Felipe
  • 625
  • 7
  • 24
  • 1
    Thank you for this answer. I'm trying to use this but I'm getting typscript errors from AutoIncrementFactory(connection). "Argument of type Connection is not assignable to parameter of type 'Schema'. Did you run into this as well? – Todd Jul 13 '20 at 16:25
  • Hi @Todd, no I didn't encounter that problem, it seems to me you are assigning the connection to something that is not a Connection, but without more context it's difficult to tell. – Andres Felipe Jul 14 '20 at 19:00
  • thanks Andres. My module looks just like yours above. Maybe underlying libraries differ between our installations. – Todd Jul 15 '20 at 22:02
  • I did the same implementation explained in this answer, however when I generate a new orderModel, the orderId field doesn't populate. – Daggle Oct 15 '20 at 19:45
  • 1
    Ok I fixed by doing this forFeatureAsync import on the main AppModule and not on the OrderModule. It's finally working. – Daggle Oct 15 '20 at 23:26
  • 2
    @AndresFelipe Getting this error any idea? `Cannot read property 'modelNames' of undefined` – HenonoaH Oct 25 '20 at 06:04
  • @HenonoaH Same here, did you solve it? – Koray Kural Sep 25 '21 at 19:08
  • const AutoIncrement = AutoIncrementFactory(connection as any); i will fix the above issues – Abdul Jabbar Nov 29 '22 at 08:46
  • can you guys share a complete example? I'm following the sample in the database `counter` collection automatically created, but the user collection doesn't have id automatically generated, and no error Thanks! @AndresFelipe @Todd – Harry Andriyan Jul 25 '23 at 17:59
1

I faced the same problem but this worked for me:

MongooseModule.forFeatureAsync([
  {
    name: Cats.name,
    useFactory: async (connection: Connection) => {
      const schema = CatsSchema;
      const AutoIncrement = require('mongoose-sequence')(connection);
      schema.plugin(AutoIncrement, {inc_field: 'id'});
      return schema;
    },
    inject: [getConnectionToken()],
  },
]),
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
0

I run into this problem and it worked for me from the first time

@Module({
    imports: [MongooseModule.forFeatureAsync([
        {
            name: Supplier.name,
            useFactory: async (connection: Connection) =>{
                const schema = SupplierSchema;
                const AutoIncrement = require('mongoose-sequence')(connection)
                 schema.plugin(AutoIncrement, {inc_field: 'code'});
                 return schema;
            },
            inject: [getConnectionToken(process.env.DATA_BASE_URI)]
        }
    ])],
    controllers: [SupplierController],
    providers: [SupplierService]

})

but a simple questions : why would i use async when i don't have any await ?