According to redis.io, Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.
In this post, we discuss how to use Redis as a cache between the application (Node.js & ExpressJs) and MongoDB.
As a normal way, each time an API query to get data from MongoDB, we will do:
– Writing the result that we got to Redis with the format {id: “query-1”, data: {…}}
– Set an expired time for it
– Return it to Frontend
If we do like that, how can we handle in case Frontend sends an API to update the value for that data? Obviously, next time we still respond to an old one which is gotten from Redis.
Let’s think this way: if we have any way to check that the data has existed on Redis before updating it to MongoDB. Then, we can fix this case.
To do it, we should to hook to mongoose and override the exec
function. Here is an example code:
const mongoose = require('mongoose');
const Promise = require('bluebird')
const redis = Promise.promisifyAll(require('redis'))
const redisClient = redis.createClient("redis://localhost:6379")
// Magic in here
mongoose.Query.prototype.cache = function(options = {}) {
this.useCache = true;
this.hashKey = JSON.stringify(options.key || 'default' );
return this;
}
mongoose.Query.prototype.exec = async function() {
if(!this.useCache) {
return exec.apply(this, arguments);
}
const keyData = JSON.stringify (
Object.assign({}, this.getQuery(), {
collection: this.mongooseCollection.name
})
);
const isExistedValue = await redisClient.hget(this.hashKey, keyData);
if(isExistedValue) {
const data = JSON.parse(isExistedValue);
return Array.isArray(data) ? data.map(item => new this.model(item))
: new this.model(item);
}
const result = await exec.apply(this, arguments);
await redisClient.setAsync(this.hashKey, keyData, JSON.stringify(result), 'EX', 900);// expired 15 mins
return result;
}
await Product.find({....}).cache({key: req.query.id});
Well done!
Please share your thoughts on making it better.