NestJs mongoose pre hooks not triggering

Viewed 1442

pre('save') and pre('remove') Hooks not triggering in nestjs.

I just started with nestjs. I've encountered an issue in which pre hooks are not working.

I followed official nestjs documentation for mongoose. but nothing works in my case

I want to update the product when review is created, deleted, and updated. I want to inject productServiceinto hooks.

Below are the relevant snippets.

package.json

{
  "name": "server",
  "version": "0.0.1",
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^7.5.1",
    "@nestjs/config": "^0.6.1",
    "@nestjs/core": "^7.5.1",
    "@nestjs/graphql": "^7.9.4",
    "@nestjs/jwt": "^7.2.0",
    "@nestjs/mongoose": "^7.2.4",
    "@nestjs/passport": "^7.1.5",
    "@nestjs/platform-express": "^7.5.1",
    "@nestjs/serve-static": "^2.1.4",
    "@types/bcrypt": "^3.0.0",
    "apollo-server-express": "^2.19.1",
    "bcrypt": "^5.0.0",
    "class-transformer": "^0.3.2",
    "class-validator": "^0.13.1",
    "cloudinary": "^1.23.0",
    "cookie-parser": "^1.4.5",
    "graphql": "^15.4.0",
    "graphql-tools": "^7.0.2",
    "graphql-upload": "^11.0.0",
    "mongoose": "^5.11.19",
    "passport": "^0.4.1",
    "passport-jwt": "^4.0.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^6.6.3",
    "slugify": "^1.4.6",
    "stripe": "^8.132.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^7.5.1",
    "@nestjs/schematics": "^7.1.3",
    "@nestjs/testing": "^7.5.1",
    "@types/cookie-parser": "^1.4.2",
    "@types/express": "^4.17.8",
    "@types/jest": "^26.0.15",
    "@types/node": "^14.14.6",
    "@types/passport-jwt": "^3.0.3",
    "@types/supertest": "^2.0.10",
    "@typescript-eslint/eslint-plugin": "^4.6.1",
    "@typescript-eslint/parser": "^4.6.1",
    "eslint": "^7.12.1",
    "eslint-config-prettier": "7.0.0",
    "eslint-plugin-prettier": "^3.1.4",
    "jest": "^26.6.3",
    "prettier": "^2.1.2",
    "supertest": "^6.0.0",
    "ts-jest": "^26.4.3",
    "ts-loader": "^8.0.8",
    "ts-node": "^9.0.0",
    "tsconfig-paths": "^3.9.0",
    "typescript": "^4.2.3"
  },
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}

Review Module


@Module({
  imports: [
    forwardRef(() => ProductModule),
    MongooseModule.forFeatureAsync([{
      name: Review.name,
      useFactory: (
      ) => {
        const schema = ReviewSchema
        schema.pre<ReviewDoc>('save', function (next) {
          console.log("INSIDE SAVE")
          next()
        })
        schema.pre<ReviewDoc>('remove', function (next) {
          console.log("INSIDE REMOVE")
          next()
        })

        return schema

      }
    }]),

    MongooseModule.forFeature([{ name: Product.name, schema: ProductSchema }]),
    MongooseModule.forFeature([{ name: Member.name, schema: MemberSchema }]),

    MongooseModule.forFeature([{ name: Review.name, schema: ReviewSchema }])
  ],
  providers: [ReviewService, ReviewResolver],
  exports: [ReviewService]
})
export class ReviewModule { }

Review Entity

import { SchemaFactory, Schema, Prop } from "@nestjs/mongoose";
import * as mongoose from 'mongoose'
import { Member } from "../member/member.entity";
import { IReviewMember } from "./review.type";

export type ReviewDoc = Review & mongoose.Document

@Schema({ _id: false })
class ReviewMember {
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: Member.name, required: true })
  id: string;
  @Prop({ required: true })
  firstName: string;
  @Prop({ required: true })
  lastName: string;
  @Prop({ type: String })
  avatar?: string;
}
export const ReviewMemberSchema = SchemaFactory.createForClass(ReviewMember)


@Schema({
  timestamps: true,
  toJSON: {
    transform: (doc, ret) => {
      ret.id = doc._id;
      delete ret._id
      delete ret.__v
    }
  }
})
export class Review {

  @Prop({ type: ReviewMemberSchema, required: true })
  member: IReviewMember;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'products', required: true })
  productId: string;

  @Prop()
  productSlug: string;

  @Prop()
  comment: string;

  @Prop({
    set: function (rating: number) {
      this._prevRating = this.rating;
      return rating;
    }
  })
  rating: number;

}

const ReviewSchema = SchemaFactory.createForClass(Review)

export { ReviewSchema }

Review Service

async createReview({ comment, member: user, productId, rating }: ICreateReview) {

    const member = await this.members.findById(user.id)
    if (!member) {
      throw new NotFoundException(`no member found with member id "${productId}"`)
    }
    const product = await this.products.findById(productId)

    if (!product) {
      throw new NotFoundException(`no product found with product id "${productId}"`)
    }

    const review = await this.reviews.findOne({ "member.id": member.id, productId: product._id })

    if (review) {
      throw new ConflictException(`review already exist`)
    }
    // should trigger pre save  but not working
    const newReview = await this.reviews.create(
      {
        productId: product._id,
        productSlug: product.slug,
        member: {
          firstName: member.firstName,
          lastName: member.lastName,
          avatar: member.avatar,
          id: member._id
        },
        rating,
        comment,
      }
    )

    if (product.numOfReviews < 3) {
      const { comment, member, rating, _id } = newReview
      product.reviews.push({ comment, member, rating, id: _id })
      await product.save()
    }

    return newReview;

  }```
0 Answers
Related