npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-patch-mongoose

v2.6.6

Published

Patch history & events for mongoose models

Downloads

624

Readme

ts-patch-mongoose

Patch history (audit log) & events plugin for mongoose

npm npm GitHub
Coverage Quality Gate Status
Reliability Rating Maintainability Rating Security Rating

npm

Motivation

ts-patch-mongoose is a plugin for mongoose
I need to track changes of mongoose models and save them as patch history (audit log) in separate collection. Changes must also emit events that I can subscribe to and react in other parts of my application. I also want to omit some fields from patch history.

Supports and tested with

{
  "node": "18.x || 20.x || 22.x",
  "mongoose": ">=6.6.x || 7.x || 8.x",
}

Features

  • [x] Track changes in mongoose models
  • [x] Save changes in a separate collection as a patch history
  • [x] Emit events when a model is created, updated or deleted
  • [x] Omit fields that you don't want to track in patch history
  • [x] Subscribe to one/many types of event
  • [x] Use events or patch history or both
  • [x] Supports ESM and CommonJS

Installation

  • Locally inside your project
npm install ts-patch-mongoose
pnpm add ts-patch-mongoose
yarn add ts-patch-mongoose
bun add ts-patch-mongoose
  • This plugin requires mongoose >=6.6.x || 7.x || 8.x to be installed as a peer dependency
# For latest mongoose 6
npm install mongoose@6
pnpm add mongoose@6
yarn add mongoose@6
bun add mongoose@6
# For latest mongoose 7
npm install mongoose@7
pnpm add mongoose@7
yarn add mongoose@7
bun add mongoose@7
# For latest mongoose 8
npm install mongoose@8
pnpm add mongoose@8
yarn add mongoose@8
bun add mongoose@8

Example

How to use it with express ts-express-swc

Create your event constants events.ts

export const BOOK_CREATED = 'book-created'
export const BOOK_UPDATED = 'book-updated'
export const BOOK_DELETED = 'book-deleted'

Create your interface IBook.ts

import type { Types } from 'mongoose'

interface IBook {
  title: string
  description?: string
  authorId: Types.ObjectId
  createdAt?: Date
  updatedAt?: Date
}

export default IBook

Setup your mongoose model Book.ts

import { Schema, model } from 'mongoose'

import type { HydratedDocument, Types } from 'mongoose'
import type IBook from '../interfaces/IBook'

import { patchHistoryPlugin } from 'ts-patch-mongoose'
import { BOOK_CREATED, BOOK_UPDATED, BOOK_DELETED } from '../constants/events'

const BookSchema = new Schema<IBook>({
  name: {
    title: String,
    required: true
  },
  description: {
    type: String,
  },
  authorId: {
    type: Types.ObjectId,
    required: true
  }
}, { timestamps: true })

BookSchema.plugin(patchHistoryPlugin, { 
  // Provide your event constants to plugin
  eventCreated: BOOK_CREATED,
  eventUpdated: BOOK_UPDATED,
  eventDeleted: BOOK_DELETED,
  
  // You can omit some properties in case you don't want to save them to patch history
  omit: ['__v', 'createdAt', 'updatedAt'],

  // Addition options for patchHistoryPlugin plugin
  // Everything bellow is optional and just shows you what you can do:

  // Code bellow is abstract example, you can use any other way to get user, reason, metadata
  // These three properties will be added to patch history document automatically and give you flexibility to track who, why and when made changes to your documents
  getUser: async () => {
    // For example: get user from http context
    // You should return an object, in case you want to save user to patch history
    return httpContext.get('user') as Record<string, unknown>
  },

  // Reason of document (create/update/delete) like: 'Excel upload', 'Manual update', 'API call', etc.
  getReason: async () => {
    // For example: get reason from http context, or any other place of your application
    // You shout return a string, in case you want to save reason to patch history
    return httpContext.get('reason') as string
  },

  // You can provide any information you want to save in along with patch history
  getMetadata: async () => {
    // For example: get metadata from http context, or any other place of your application
    // You should return an object, in case you want to save metadata to patch history
    return httpContext.get('metadata') as Record<string, unknown>
  },

  // Do something before deleting documents
  // This method will be executed before deleting document or documents and always returns a nonempty array of documents
  preDelete: async (docs) => {
    const bookIds = docs.map((doc) => doc._id)
    await SomeOtherModel.deleteMany({ bookId: { $in: bookIds } })
  },

  // In case you just want to track changes in your models using events below.
  // And don't want to save changes to patch history collection
  patchHistoryDisabled: true, 
})

const Book = model('Book', BookSchema)

export default Book

Subscribe

You can subscribe to events using patchEventEmitter anywhere in your application handlers/BookHandler.ts

import { patchEventEmitter } from 'ts-patch-mongoose'
import { BOOK_CREATED, BOOK_UPDATED, BOOK_DELETED } from '../constants/events'

patchEventEmitter.on(BOOK_CREATED, ({ doc }) => {
  try {
    console.log('Event - book created', doc)
    // Do something with doc here
  } catch (error) {
    console.error(error)
  }
})

patchEventEmitter.on(BOOK_UPDATED, ({ doc, oldDoc, patch }) => {
  try {
    console.log('Event - book updated', doc, oldDoc, patch)
    // Do something with doc, oldDoc and patch here
  } catch (error) {
    console.error(error)
  }
})

patchEventEmitter.on(BOOK_DELETED, ({ oldDoc }) => {
  try {
    console.log('Event - book deleted', oldDoc)
    // Do something with doc here
  } catch (error) {
    console.error(error)
  }
})

Check my other projects