Redis caching with Mongoose

Ayanabilothman - Oct 1 - - Dev Community

If you are working with NoSQL databases and use the Mongoose package as an ODM to execute the queries on the database. This article is for you.

Why caching?

Caching is essential in optimizing performance, improving scalability, and enhancing the user experience of modern applications. It can significantly reduce the load on backend resources such as databases, or APIs. By serving cached data instead of executing resource-intensive operations.

How can we apply caching with Mongoose?

mongoose object imported from the mongoose package has a powerful other object called Query. We can add to its prototype any customized method. We will take advantage of this by creating a cache method, to be able to apply it on any query we need to cache its results.

As shown in the following code:

import mongoose from "mongoose";

mongoose.Query.prototype.cache = async function () {
  // generate unique redis key
  const filterObj = this._conditions;
  const options = this.options;
  const modelName = this.mongooseCollection.name;
  const findMethod = this.op;
  const redisKey = JSON.stringify({
    ...filterObj,
    ...options,
    ...{ model: modelName },
    ...{ method: findMethod },
  });

  // check if the result is chached before
  const cached = await client.hGet(modelName, redisKey);

  if (!cached) {
    const result = await mongoose.Query.prototype.exec.apply(this, arguments);
    client.hSet(modelName, redisKey, JSON.stringify(result));
    return result;
  }

  // cache the results
  const cachedResults = JSON.parse(cached);
  return Array.isArray(cachedResults)
    ? cachedResults.map((doc) => this.model.hydrate(doc))
    : this.model.hydrate(cachedResults);
};

app.get("/user", async (req, res) => {
  const users = await User.findOne({ age: { $gt: 30 } }).cache();
  return res.json({ results: users });
});
Enter fullscreen mode Exit fullscreen mode

In this code, we create a cache method that easily can be applied to any query. The logic of this method is simple :

1- generate a unique key name.

2- check if this key has a value cached before.

3- if yes, return the cached result.

4- if no, cache the query's result and return it.

Knowing that, the values are saved in a hash table, to optimize the performance.

With Query prototype we can add paginate method, for instance, or any other customized method to be applied to a query and write more clean code.

. . . . .
Terabox Video Player