mongoose-plugin-cache
Seamlessly boost your MongoDB performance with Redis
Why mongoose-plugin-cache?
- Performance: Significantly enhance the overall User Experience by resolving the data from memory.
- Efficiency: Cache with peace of mind. It handles the cache synchronization with Mongoose
create
,findByIdAndUpdate
,findOneAndUpdate
,findByIdAndDelete
andfindOneAndDelete
hooks, so you don't have to. - Flexible: Enable only the model you want to cache as well as specifying the additional cache keys to resolve.
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.
Installation
yarn add mongoose-plugin-cache
Getting Started
schema.plugin createCachePlugin,
Basic Usage
Resolving from Cache
It 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.
With Mongoose only, we normally do:
Instead of using findById
or findOne
, an extra methods get
is provided for cache retrieval:
Batch Operation
It 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.
With Mongoose only, we do:
An extra method getMany
is provided for batch cache retrieval:
Clearing the Cache
Clearing the cache will only remove the matching cache in Redis. The data in the database is not affected.
await User.clear'<userId>'await User.clearMany
Advance Usage
Additional Cache Keys
Sometimes 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.
schema.plugin createCachePlugin, // getBy with an extra param is equivalent to getBySlugawait Entry.getBy'slug', '<slug>'await Entry.getBySlug'<slug>' // it also supports batchingawait Entry.getBySlugawait Entry.getBySlugs
Metrics
Sometimes, you may want to be notified when there is a cache miss or data miss event to strengthen the control over the data.
schema.plugin createCachePlugin,
Using with Dataloader and GraphQL
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.
And call it with:
await userLoader.load'<userId>'
With GraphQL's field resolver, you don't even have to use Mongoose's .populate()
with better Separation of Concern.
Consider the following Mongoose schema design:
And the following GraphQL type definition:
type Entry { id: ID! slug: ID! title: String author: User}
We can resolve the actual User using GraphQL field resolver with the combination with Dataloader:
Testing
yarn test
Related Projects
- mongoose-redis-cache: Not actively maintained since 2014.
- Cachegoose
- mongoose-cache
- mongoose-cachebox
- mongoose-cache-manager
Contributing
Please read CONTRIBUTING.md for details, and feel free to submit pull requests to us.