{"id":14975625,"url":"https://github.com/yhjor/mongoose-plugin-cache","last_synced_at":"2025-10-27T14:31:11.482Z","repository":{"id":33397424,"uuid":"154328171","full_name":"yhjor/mongoose-plugin-cache","owner":"yhjor","description":"The Perfect Marriage of MongoDB and Redis","archived":false,"fork":false,"pushed_at":"2023-01-04T17:54:54.000Z","size":2737,"stargazers_count":44,"open_issues_count":22,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-09-28T20:42:41.454Z","etag":null,"topics":["batch","cache","graphql","mongodb","mongoose","mongoose-plugin","nodejs","redis","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yhjor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-23T12:57:18.000Z","updated_at":"2024-03-13T21:20:48.000Z","dependencies_parsed_at":"2023-01-15T00:45:06.488Z","dependency_job_id":null,"html_url":"https://github.com/yhjor/mongoose-plugin-cache","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhjor%2Fmongoose-plugin-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhjor%2Fmongoose-plugin-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhjor%2Fmongoose-plugin-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhjor%2Fmongoose-plugin-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yhjor","download_url":"https://codeload.github.com/yhjor/mongoose-plugin-cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219861083,"owners_count":16556007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batch","cache","graphql","mongodb","mongoose","mongoose-plugin","nodejs","redis","typescript"],"created_at":"2024-09-24T13:52:18.280Z","updated_at":"2025-10-27T14:31:06.018Z","avatar_url":"https://github.com/yhjor.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mongoose-plugin-cache\n\nSeamlessly boost your MongoDB performance with Redis\n\n[![Build Status](https://travis-ci.org/yhjor/mongoose-plugin-cache.svg?branch=master)](https://travis-ci.org/yhjor/mongoose-plugin-cache)\n[![codecov](https://codecov.io/gh/yhjor/mongoose-plugin-cache/branch/master/graph/badge.svg)](https://codecov.io/gh/yhjor/mongoose-plugin-cache)\n\n## Why mongoose-plugin-cache?\n\n- **Performance**: Significantly enhance the overall User Experience by resolving the data from memory.\n- **Efficiency**: Cache with peace of mind. It handles the cache synchronization with Mongoose `create`, `findByIdAndUpdate`, `findOneAndUpdate`, `findByIdAndDelete` and `findOneAndDelete` hooks, so you don't have to.\n- **Flexible**: Enable only the model you want to cache as well as specifying the additional cache keys to resolve.\n\n##\n\n\u003e Prerequisite: Mongoose 5. One of the biggest updates from Mongoose 4 to 5 is the synchronous and stability of hook, which helps get the data in sync easily.\n\n## Installation\n\n```bash\nyarn add mongoose-plugin-cache\n```\n\n## Getting Started\n\n```typescript\nimport mongoose from 'mongoose'\nimport createCachePlugin from 'mongoose-plugin-cache'\nimport redis from './redis'\n\nconst schema = new mongoose.Schema({\n  name: String,\n  email: String,\n})\n\nschema.plugin(\n  createCachePlugin({\n    // your own node_redis instance\n    // keep all your preferences like cache prefix, caching strategy, and global promise to be used\n    redis,\n    // it will use Redis only if you enable it (default: false),\n    // and you may only want to enable for the model with high frequency database access\n    enable: true,\n  }),\n)\n\nconst User = mongoose.model('User', schema)\n```\n\n## Basic Usage\n\n### Resolving from Cache\n\nIt first tries to resolve the value from the cache by a given ID. If it hits the cache, the value will be returned directly from Redis. If it does not hit the cache, it will resolve the data from the database and set it into Redis, `onCacheMiss` will be called. If there is no such data, `onDataMiss` hook will be called.\n\nWith Mongoose only, we normally do:\n\n```typescript\nconst user = await User.findById('\u003cuserId\u003e')\n```\n\nInstead of using `findById` or `findOne`, an extra methods `get` is provided for cache retrieval:\n\n```typescript\nconst user = await User.get('\u003cuserId\u003e')\n```\n\n### Batch Operation\n\nIt performs the same cache resolve logic, but the responses will always match their corresponding ID index location and resolves it with `null` if there is no data from the Database. It also runs data retrieval for those who have cache miss in batch to reduce the IO operation.\n\nWith Mongoose only, we do:\n\n```typescript\nconst userIds = ['\u003cuserId1\u003e', '\u003cuserId2\u003e']\n\nconst users = await User.find({\n  _id: {\n    $in: userIds,\n  },\n})\n```\n\nAn extra method `getMany` is provided for batch cache retrieval:\n\n```typescript\nconst users = await User.getMany(userIds)\n```\n\n### Clearing the Cache\n\nClearing the cache will only remove the matching cache in Redis. The data in the database is not affected.\n\n```typescript\nawait User.clear('\u003cuserId\u003e')\nawait User.clearMany(['\u003cuserId1\u003e', '\u003cuserId2\u003e', '\u003cslug1\u003e', '\u003cslug2\u003e'])\n```\n\n## Advance Usage\n\n### Additional Cache Keys\n\nSometimes we might use fields other than `_id` to resolve the data. For instance, `username` and `email` are often considered unique in a User model. Plus, for security reason, the client application normally does not manipulate the ID directly. Instead of mapping the actual ID to a particular field, you can provide an option called `additionalCacheKeys` to the plugin, and it will add an index to MongoDB and map it with the corresponding `_id` for the resolve.\n\n```typescript\nschema.plugin(\n  createCachePlugin({\n    ...options,\n    additionalCacheKeys: ['slug'],\n  }),\n)\n\nconst Entry = mongoose.model('Entry', schema)\n\n// getBy with an extra param is equivalent to getBySlug\nawait Entry.getBy('slug', '\u003cslug\u003e')\nawait Entry.getBySlug('\u003cslug\u003e')\n\n// it also supports batching\nawait Entry.getBySlug(['\u003cslug1\u003e', '\u003cslug2\u003e'])\nawait Entry.getBySlugs(['\u003cslug1\u003e', '\u003cslug2\u003e'])\n```\n\n### Metrics\n\nSometimes, you may want to be notified when there is a cache miss or data miss event to strengthen the control over the data.\n\n```typescript\nschema.plugin(\n  createCachePlugin({\n    ...options,\n    onCacheMiss: (modelName: string, key: string) =\u003e {\n      console.log(`cache_miss.${modelName}.${key}`)\n    },\n    onDataMiss: (modelName: string, key: string) =\u003e {\n      console.log(`cache_data_miss.${modelName}.${key}`)\n    },\n  }),\n)\n```\n\n### Using with Dataloader and GraphQL\n\n`mongoose-plugin-cache` works perfectly with Dataloader and GraphQL. It is encouraged to create a new DataLoader per request and combines it with the shared cache compatibility with `mongoose-plugin-cache` to further reduce the number of database access.\n\n```typescript\nimport Dataloader from 'dataloader'\nconst userLoader = new DataLoader(ids =\u003e User.getMany(ids))\n```\n\nAnd call it with:\n\n```typescript\nawait userLoader.load('\u003cuserId\u003e')\n```\n\nWith GraphQL's field resolver, you don't even have to use Mongoose's `.populate()` with better Separation of Concern.\n\nConsider the following Mongoose schema design:\n\n```typescript\n{\n  ...userFields,\n  authorId: { type: Schema.Types.ObjectId, ref: 'User' }\n}\n```\n\nAnd the following GraphQL type definition:\n\n```graphql\ntype Entry {\n  id: ID!\n  slug: ID!\n  title: String\n  author: User\n}\n```\n\nWe can resolve the actual User using GraphQL field resolver with the combination with Dataloader:\n\n```typescript\n{\n  author: ({authorId}, _, {userLoader}) =\u003e userLoader.load(authorId),\n}\n```\n\n## Testing\n\n```sh\nyarn test\n```\n\n## Related Projects\n\n- [mongoose-redis-cache](https://github.com/conancat/mongoose-redis-cache): Not actively maintained since 2014.\n- [Cachegoose](https://github.com/boblauer/cachegoose)\n- [mongoose-cache](https://github.com/Gottox/mongoose-cache)\n- [mongoose-cachebox](https://github.com/cayasso/mongoose-cachebox)\n- [mongoose-cache-manager](https://github.com/englercj/mongoose-cache-manager)\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](https://github.com/yhjor/mongoose-plugin-cache/blob/master/CONTRIBUTING.md) for details, and feel free to submit pull requests to us.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyhjor%2Fmongoose-plugin-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyhjor%2Fmongoose-plugin-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyhjor%2Fmongoose-plugin-cache/lists"}