Andres Felipe
Andres Felipe

Reputation: 684

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.

Upvotes: 7

Views: 12386

Answers (4)

Ahmed El-Tabarani
Ahmed El-Tabarani

Reputation: 763

At this time 2023 Sept, mongoose-sequence does not work with mongoose 7 refer to this issue

it does not work with mongoose 7. Schema.create hangs. seems to be related to callbacks being deprecated.

However there is a new package @typegoose/auto-increment https://www.npmjs.com/package/@typegoose/auto-increment

npm i @typegoose/auto-increment

How to use

import { Module } from '@nestjs/common';
import { MongooseModule, getConnectionToken } from '@nestjs/mongoose';
import { User, UserSchema } from './model/user.model';
import { UsersController } from './controller/users.controller';
import { UsersService } from './service/Users.service';
import {
  AutoIncrementID,
  AutoIncrementIDOptions,
} from '@typegoose/auto-increment';

@Module({
  imports: [
    MongooseModule.forFeatureAsync([
      {
        name: User.name,
        useFactory: async () => {
          const schema = UserSchema;

          schema.plugin(AutoIncrementID, {
            field: 'numbering',
            startAt: 1,
          } satisfies AutoIncrementIDOptions);

          return schema;
        },
        inject: [getConnectionToken()],
      },
    ]),
  ],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

Upvotes: 0

Marinos TBH
Marinos TBH

Reputation: 55

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 ?

Upvotes: 0

Valliyappan
Valliyappan

Reputation: 31

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()],
  },
]),

Upvotes: 3

Andres Felipe
Andres Felipe

Reputation: 684

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 {}

Upvotes: 11

Related Questions