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 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-cache-mongoose

v2.2.1

Published

Cache plugin for mongoose Queries and Aggregate (in-memory, redis)

Readme

ts-cache-mongoose

Cache query and aggregate in mongoose using in-memory or redis

npm npm GitHub
Coverage Quality Gate Status
Reliability Rating Maintainability Rating Security Rating
Socket Badge OpenSSF Scorecard OpenSSF Best Practices

Motivation

ts-cache-mongoose is a plugin for mongoose
I need a way to cache mongoose queries and aggregations to improve application performance. It should support both in-memory and Redis cache engines, work with all major Node.js frameworks, and be easy to use with a simple .cache() method on queries and aggregations.

Supports and tested with

{
  "node": "20.x || 22.x || 24.x",
  "mongoose": ">=6.6.0 <10"
}

CI tests against mongoose 6.12.2, 7.6.4, 8.23.0, and 9.4.1.

Features

  • In-memory caching
  • Redis caching
  • Cache expiration
  • Cache invalidation
  • Cache key generation
  • Cache key prefix
  • Query caching
  • Aggregate caching
  • Supports ESM and CommonJS

Installation

mongoose is a peer dependency — install it alongside ts-cache-mongoose.

npm install ts-cache-mongoose mongoose
pnpm add ts-cache-mongoose mongoose
yarn add ts-cache-mongoose mongoose
bun add ts-cache-mongoose mongoose

Example

Works with any Node.js framework — Express, Fastify, Koa, Hono, etc:

import mongoose from 'mongoose'
import cache from 'ts-cache-mongoose'

// In-memory
cache.init(mongoose, {
  defaultTTL: '60 seconds',
  engine: 'memory',
})

// Or Redis
cache.init(mongoose, {
  defaultTTL: '60 seconds',
  engine: 'redis',
  engineOptions: {
    host: 'localhost',
    port: 6379,
  },
})

mongoose.connect('mongodb://localhost:27017/my-database')

Query caching

const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
const book = await Book.findById(id).cache('1 hour').exec()
const count = await Book.countDocuments().cache('1 minute').exec()
const authors = await Book.distinct('author').cache('30 seconds').exec()

Aggregate caching

const books = await Book.aggregate([
  { $match: { genre: 'fantasy' } },
  { $group: { _id: '$author', count: { $sum: 1 } } },
]).cache('1 minute').exec()

Bounded in-memory cache

The in-memory engine is unbounded by default. For workloads where query keys are driven by user input (search, filters, pagination), cap the cache so a caller generating unique cache keys cannot grow the map without limit. Two bounds are available and can be combined — eviction is LRU, and whichever bound is hit first triggers it:

cache.init(mongoose, {
  engine: 'memory',
  defaultTTL: '60 seconds',
  maxEntries: 10_000,          // cap by entry count
  maxBytes: 50 * 1024 * 1024,  // cap by serialized bytes (50 MB)
})

maxBytes measures entry size via node:v8.serialize(value).byteLength by default — handles circular references (mongoose populate parent-refs), single C++ call per set, works on Node / Bun / Deno. Provide your own sizeCalculation callback if you want an O(1) estimate instead:

cache.init(mongoose, {
  engine: 'memory',
  maxBytes: 50 * 1024 * 1024,
  sizeCalculation: (value) => {
    if (Array.isArray(value)) return value.length * 512
    return 512
  },
})

Eviction is soft: the just-written entry is never dropped, even if its own size exceeds maxBytes. Everything older gets evicted until both bounds are satisfied (or only the new entry remains).

Both options are ignored for the Redis engine — use Redis's own maxmemory + maxmemory-policy instead.

Custom error handling

By default, cache engine failures (Redis disconnects, serialization errors, etc.) are logged via console.error and the query falls through to the database. Pass an onError callback to route them somewhere else — e.g. a structured logger, Sentry, or a metric counter:

cache.init(mongoose, {
  engine: 'redis',
  defaultTTL: '60 seconds',
  engineOptions: { host: 'localhost', port: 6379 },
  onError: (error) => {
    logger.warn({ err: error }, 'cache engine failure')
  },
})

The callback receives the raw Error. Cache reads and writes never throw — a failing engine degrades to a cache miss.

Cache invalidation

const instance = cache.init(mongoose, { engine: 'memory', defaultTTL: '60 seconds' })

// Clear all cache
await instance.clear()

// Or use custom cache key
const user = await User.findById(id).cache('1 minute', 'user-key').exec()
await instance.clear('user-key')

NestJS (because it's special)

Import CacheModule from ts-cache-mongoose/nest:

import { CacheModule } from 'ts-cache-mongoose/nest'

@Module({
  imports: [
    MongooseModule.forRoot(process.env.MONGO_URI),
    CacheModule.forRoot({
      engine: 'memory',
      defaultTTL: '60 seconds',
    }),
  ],
})
export class AppModule {}

With ConfigService:

CacheModule.forRootAsync({
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    engine: config.get('CACHE_ENGINE', 'memory'),
    defaultTTL: config.get('CACHE_TTL', '60 seconds'),
  }),
})

Inject CacheService for programmatic cache clearing:

import { CacheService } from 'ts-cache-mongoose/nest'

@Injectable()
export class SomeService {
  constructor(private readonly cacheService: CacheService) {}

  async clearUserCache() {
    await this.cacheService.clear('user-cache-key')
  }
}

Contributing

Check CONTRIBUTING.md

License

This project is licensed under the MIT License - see the LICENSE file for details

Check my other projects