https://github.com/ilovepixelart/ts-cache-mongoose
Cache plugin for mongoose Queries and Aggregate (in-memory, redis)
https://github.com/ilovepixelart/ts-cache-mongoose
aggregate backend cache db memory mongdodb mongo mongoose nodejs nosql plugin query redis schema store ts ts-cache-mongoose ttl typescript
Last synced: about 1 month ago
JSON representation
Cache plugin for mongoose Queries and Aggregate (in-memory, redis)
- Host: GitHub
- URL: https://github.com/ilovepixelart/ts-cache-mongoose
- Owner: ilovepixelart
- License: mit
- Created: 2023-04-26T21:35:53.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2026-04-12T23:23:01.000Z (2 months ago)
- Last Synced: 2026-04-13T00:36:10.107Z (2 months ago)
- Topics: aggregate, backend, cache, db, memory, mongdodb, mongo, mongoose, nodejs, nosql, plugin, query, redis, schema, store, ts, ts-cache-mongoose, ttl, typescript
- Language: TypeScript
- Homepage:
- Size: 1.49 MB
- Stars: 34
- Watchers: 1
- Forks: 7
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
Awesome Lists containing this project
- awesome-mongoose-plugins - ts-cache-mongoose - cache-mongoose?style=flat&label=%20) |  |  | Cache query and aggregate in mongoose using in-memory or redis. | (⚡ Caching & Performance)
README
# ts-cache-mongoose
Cache query and aggregate in mongoose using in-memory or redis
[](https://www.npmjs.com/package/ts-cache-mongoose)
[](https://www.npmjs.com/package/ts-cache-mongoose)
[](https://github.com/ilovepixelart/ts-cache-mongoose/blob/main/LICENSE)
\
[](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
[](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
\
[](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
[](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
[](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
\
[](https://socket.dev/npm/package/ts-cache-mongoose)
[](https://securityscorecards.dev/viewer/?uri=github.com/ilovepixelart/ts-cache-mongoose)
[](https://www.bestpractices.dev/en/projects/12484)
## 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
```json
{
"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`.
```bash
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:
```typescript
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
```typescript
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
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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
```typescript
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`:
```typescript
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`:
```typescript
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:
```typescript
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](CONTRIBUTING.md)
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
## Check my other projects
- [ts-migrate-mongoose](https://github.com/ilovepixelart/ts-migrate-mongoose) - Migration framework for mongoose
- [ts-patch-mongoose](https://github.com/ilovepixelart/ts-patch-mongoose) - Patch history & events plugin for mongoose