10

How can I implement the mongoose plugin using nestjs?

import * as mongoose from 'mongoose';
import uniqueValidator from 'mongoose-unique-validator';
import mongoosePaginate from 'mongoose-paginate';
import mongoose_delete from 'mongoose-delete';

const UsuarioSchema = new mongoose.Schema({
    username: {
        type: String,
        unique: true,
        required: [true, 'El nombre de usuario es requerido']
    },
    password: {
        type: String,
        required: [true, 'La clave es requerida'],
        select: false
    }
});

UsuarioSchema.plugin(uniqueValidator, { message: '{PATH} debe ser único' });
UsuarioSchema.plugin(mongoosePaginate);
UsuarioSchema.plugin(mongoose_delete, { deletedAt : true, deletedBy : true, overrideMethods: true });

Error: First param to schema.plugin() must be a function, got "undefined"

6 Answers6

18

This is a snippet for those who are using mongoose-paginate plugin with nestjs. You can also install @types/mongoose-paginate for getting the typings support

  1. Code for adding the paginate plugin to the schema:
import { Schema } from 'mongoose';
import * as mongoosePaginate from 'mongoose-paginate';

export const MessageSchema = new Schema({
// Your schema definitions here
});

// Register plugin with the schema
MessageSchema.plugin(mongoosePaginate);
  1. Now in the Message interface document
export interface Message extends Document {
// Your schema fields here
}

  1. Now you can easily get the paginate method inside the service class like so
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { PaginateModel } from 'mongoose';
import { Message } from './interfaces/message.interface';

@Injectable()
export class MessagesService {
    constructor(
        // The 'PaginateModel' will provide the necessary pagination methods
        @InjectModel('Message') private readonly messageModel: PaginateModel<Message>,
    ) {}

    /**
     * Find all messages in a channel
     *
     * @param {string} channelId
     * @param {number} [page=1]
     * @param {number} [limit=10]
     * @returns
     * @memberof MessagesService
     */
    async findAllByChannelIdPaginated(channelId: string, page: number = 1, limit: number = 10) {
        const options = {
            populate: [
                // Your foreign key fields to populate
            ],
            page: Number(page),
            limit: Number(limit),
        };
        // Get the data from database
        return await this.messageModel.paginate({ channel: channelId }, options);
    }
}
Sandeep K Nair
  • 2,512
  • 26
  • 26
  • Although I have this error, but working fine => Property 'paginate' does not exist on type 'Model'.ts(2339) – Ali Bahrami May 12 '20 at 11:34
  • That's because I used Model instead of PaginateModel inside constructor. Thanks! – Ali Bahrami Jun 25 '20 at 07:19
  • 1
    Hi, I need the same but with __mongoose-aggregate-paginate-v2__, this example doesn't works and doesn't exist @types. Can you help me? – Hector Sep 10 '20 at 18:05
  • Hi @Hector sorry, i am not familiar with that plugin. Probably you can look at the source code for the same and create your own typings file and use it in your project internally. Don't really have to depend on external typings always. – Sandeep K Nair Sep 12 '20 at 07:55
  • @SandeepKNair can you show your `message.provider.ts`? I am unsure how you're satisfying the `@Inject('Message')` Thanks! – Adam Cox Jan 13 '21 at 08:18
  • @AdamCox there isn't any difference in how `Message` is injected to the module provider section. ```typescript imports: [ MongooseModule.forFeature([ { name: 'Message', schema: MessageSchema }, ]) ] ``` – Sandeep K Nair Jan 13 '21 at 08:31
  • @SandeepKNair thanks. I am working on similar but using what Hector mentioned.. SO ques: https://stackoverflow.com/questions/65669164/how-to-provide-server-side-pagination-with-nestjs – Adam Cox Jan 13 '21 at 09:40
  • @AdamCox sorry I thought you were asking about `mongoose-paginate-v2`. I haven't used `mongoose-aggregate-paginate-v2` plugin personally on any projects yet. But ideally there would be a wrapper interface type for aggregate-paginate plugin like `PaginateModel` in mongoose-paginate if I am not wrong. You will have to find it in the @types that you installed `@types/mongoose-aggregate-paginate-v2` as npm dependency. – Sandeep K Nair Jan 13 '21 at 10:22
3

NestJS Documentation has better way to add plugins to either individual schema.

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

Or if at global level.

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/test', {
      connectionFactory: (connection) => {
        connection.plugin(require('mongoose-autopopulate'));
        return connection;
      }
    }),
  ],
})
export class AppModule {}
tamir
  • 45
  • 6
  • 3
    This is great. But curious how to add it in if using this Mongooge import format `MongooseModule.forRootAsync({useFactory: (configService: ConfigService) => ({uri: "blah", user: "blah", pass: "blah"}), inject: [ConfigService] });` – wongz Apr 23 '22 at 20:40
  • 1
    @wongz check this https://stackoverflow.com/a/75739750/10806551 – olawalejuwonm Mar 16 '23 at 23:18
2

Try this:

import * as mongoose from 'mongoose';
import * as uniqueValidator from 'mongoose-unique-validator';
import * as mongoosePaginate from 'mongoose-paginate';
import * as mongoose_delete from 'mongoose-delete';

const UsuarioSchema = new mongoose.Schema({
   username: {
    type: String,
    unique: true,
    required: [true, 'El nombre de usuario es requerido']
   },
   password: {
       type: String,
       required: [true, 'La clave es requerida'],
       select: false
   }
});

UsuarioSchema.plugin(uniqueValidator, { message: '{PATH} debe ser único' });
UsuarioSchema.plugin(mongoosePaginate);
UsuarioSchema.plugin(mongoose_delete, { deletedAt : true, deletedBy : true, overrideMethods: true });

export default UsuarioSchema;

Then you can use it like this:

import UsuarioSchema from './UsuarioSchema'
2

If you're using forRootAsync, This worked for me as below

MongooseModule.forRootAsync({
  useFactory: async (ConfigService: ConfigService) => {
    const connectionName: string =
      process.env.NODE_ENV === 'production'
        ? 'DATABASE_URL'
        : 'LOCAL_DATABASE_URL';

    mongoose.plugin(require('mongoose-unique-validator')); // import mongoose the normal way and you can import your plugin as desired
    return {
      uri: ConfigService.get<string>(connectionName),
    };
  },
  inject: [ConfigService],
}),
olawalejuwonm
  • 1,315
  • 11
  • 17
0

here is an example of using timestamp plugin

import { Schema } from 'mongoose';
import * as timestamp from 'mongoose-timestamp';
export const ConversationSchema = new Schema({
  users: [String],
}).plugin(timestamp);

try replace

import uniqueValidator from 'mongoose-unique-validator';
import mongoosePaginate from 'mongoose-paginate';
import mongoose_delete from 'mongoose-delete';

by

import * as uniqueValidator from 'mongoose-unique-validator';
import * as mongoosePaginate from 'mongoose-paginate';
import * as mongoose_delete from 'mongoose-delete';
Shady Khalifa
  • 177
  • 4
  • 7
-2

If you followed the official doc, you can add plugins in this file:

`export const databaseProviders = [
  {
    provide: 'DbConnectionToken',
    useFactory: async () => {
      (mongoose as any).Promise = global.Promise;

      mongoose
        .plugin('pluginOne')
        .plugin('pluginTwo')

      return await mongoose.connect('mongodb://localhost/nest', {
        useMongoClient: true,
      });
    },
  },
];`

Remind, If you set the plugins in the Schema file, you set the same plugins as many times. The best way to set plugins is only once.

aperdizs
  • 106
  • 6