All Projects → yhjor → mongoose-plugin-cache

yhjor / mongoose-plugin-cache

Licence: MIT license
The Perfect Marriage of MongoDB and Redis

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to mongoose-plugin-cache

historical
A Mongoose plugin that archives document diffs and manages document history.
Stars: ✭ 33 (-21.43%)
Mutual labels:  mongoose, mongoose-plugin
mongoose-pii
A Mongoose plugin that lets you transparently cipher stored PII and use securely-hashed passwords
Stars: ✭ 43 (+2.38%)
Mutual labels:  mongoose, mongoose-plugin
mongoose-keywords
Mongoose plugin that generates a keywords path combining other paths values
Stars: ✭ 23 (-45.24%)
Mutual labels:  mongoose, mongoose-plugin
nuada-cli
Nuada CLI was designed to improve your development experience by using ExpressJS and Mongoose tools.
Stars: ✭ 19 (-54.76%)
Mutual labels:  mongoose
how-to-build-your-own-uber-for-x-app
Code for my blog post on How to Build Your Own Uber-for-X App
Stars: ✭ 138 (+228.57%)
Mutual labels:  mongoose
TvrboReact
Dream starter project: React, Redux, React Router, Webpack
Stars: ✭ 13 (-69.05%)
Mutual labels:  mongoose
ocr2text
Convert a PDF via OCR to a TXT file in UTF-8 encoding
Stars: ✭ 90 (+114.29%)
Mutual labels:  batch
add-my-name
No more WhatsApp spams 🎉
Stars: ✭ 16 (-61.9%)
Mutual labels:  mongoose
Discord-Template-V13
An easy-to-use discord bot including database, slash commands and context menus !
Stars: ✭ 103 (+145.24%)
Mutual labels:  mongoose
koa-session-mongoose
Mongoose store for Koa sessions
Stars: ✭ 29 (-30.95%)
Mutual labels:  mongoose
node-server-template
This is Node.js server tidy template / boilerplate with Express (with asyncified handlers, custom error handler) framework and MongoDb. The server use ES6 and above. On different branches you can see different techniques' and technologies' usage, such as Kafka, nodemailer, file download... You also can find postman collections.
Stars: ✭ 116 (+176.19%)
Mutual labels:  mongoose
vue-cosmosdb
Cosmos DB, Express.js, Vue, and Node.js app
Stars: ✭ 58 (+38.1%)
Mutual labels:  mongoose
Apriliya-Api
Simple Web API with user authentication
Stars: ✭ 19 (-54.76%)
Mutual labels:  mongoose
OGMNeo
[No Maintenance] Neo4j nodeJS OGM(object-graph mapping) abstraction layer
Stars: ✭ 54 (+28.57%)
Mutual labels:  batch
Discord.js-MongoDB-bot
Discord.js botları için Mongoose veritabanı (database) kullanım örnekleri.
Stars: ✭ 13 (-69.05%)
Mutual labels:  mongoose
code-examples
Example projects for my personal blog.
Stars: ✭ 144 (+242.86%)
Mutual labels:  mongoose
graphql-rest-api-demo
A demo of what an equivalent REST API and GraphQL API look like.
Stars: ✭ 51 (+21.43%)
Mutual labels:  mongoose
nodejs-scaffolding
A node.js sample application that demonstrates an architecture for building a complete production API with Node.JS, Express.JS and MongoDB
Stars: ✭ 33 (-21.43%)
Mutual labels:  mongoose
Trellis
A simplified Trello clone built with React, Redux, Node, Express and MongoDB.
Stars: ✭ 116 (+176.19%)
Mutual labels:  mongoose
mongoose-slug-updater
Schema-based slug plugin for Mongoose - single/compound - unique over collection/group - nested docs/arrays - relative/abs paths - sync on change: create/save/update/updateOne/updateMany/findOneAndUpdate tracked - $set operator - counter/shortId
Stars: ✭ 37 (-11.9%)
Mutual labels:  mongoose

mongoose-plugin-cache

Seamlessly boost your MongoDB performance with Redis

Build Status codecov

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 and findOneAndDelete 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

import mongoose from 'mongoose'
import createCachePlugin from 'mongoose-plugin-cache'
import redis from './redis'

const schema = new mongoose.Schema({
  name: String,
  email: String,
})

schema.plugin(
  createCachePlugin({
    // your own node_redis instance
    // keep all your preferences like cache prefix, caching strategy, and global promise to be used
    redis,
    // it will use Redis only if you enable it (default: false),
    // and you may only want to enable for the model with high frequency database access
    enable: true,
  }),
)

const User = mongoose.model('User', schema)

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:

const user = await User.findById('<userId>')

Instead of using findById or findOne, an extra methods get is provided for cache retrieval:

const user = await User.get('<userId>')

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:

const userIds = ['<userId1>', '<userId2>']

const users = await User.find({
  _id: {
    $in: userIds,
  },
})

An extra method getMany is provided for batch cache retrieval:

const users = await User.getMany(userIds)

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(['<userId1>', '<userId2>', '<slug1>', '<slug2>'])

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({
    ...options,
    additionalCacheKeys: ['slug'],
  }),
)

const Entry = mongoose.model('Entry', schema)

// getBy with an extra param is equivalent to getBySlug
await Entry.getBy('slug', '<slug>')
await Entry.getBySlug('<slug>')

// it also supports batching
await Entry.getBySlug(['<slug1>', '<slug2>'])
await Entry.getBySlugs(['<slug1>', '<slug2>'])

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({
    ...options,
    onCacheMiss: (modelName: string, key: string) => {
      console.log(`cache_miss.${modelName}.${key}`)
    },
    onDataMiss: (modelName: string, key: string) => {
      console.log(`cache_data_miss.${modelName}.${key}`)
    },
  }),
)

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.

import Dataloader from 'dataloader'
const userLoader = new DataLoader(ids => User.getMany(ids))

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:

{
  ...userFields,
  authorId: { type: Schema.Types.ObjectId, ref: 'User' }
}

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:

{
  author: ({authorId}, _, {userLoader}) => userLoader.load(authorId),
}

Testing

yarn test

Related Projects

Contributing

Please read CONTRIBUTING.md for details, and feel free to submit pull requests to us.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].