| modified | Friday 3 October 2025 |
|---|
Caching is a technique to store a copy of the data in a faster storage system (i.e. cache) to speed up the data retrieval.
Caching is used to store the data that the users frequently access. It helps reduce the load on the main storage system and improves the application’s performance.
Redis is an open-source in-memory data structure store that can be used as a database, cache, and message broker.
It supports various data structures such as strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, and geospatial indexes with radius queries.
If you have not installed Redis, you can use https://console.upstash.com/ to create a Redis server.
Get the connection string from the dashboard.
Create a new node.js application with express.
Or you can use my node starter template Node-ENV
redis package using yarn.1yarn add ioredis @types/ioredis
note: i use yarn because it’s faster than npm and can install packages in parallel.
serve the Redis server connection string in the .env file or use the connection string directly in the code.
Create a new file redis.ts and connect to the Redis server.
1import dotenv from 'dotenv';
2import Redis from 'ioredis';
3import { LOGGER } from '../logging';
4
5dotenv.config({ path: `${process.cwd()}/.env` });
6
7const { REDIS_URL } = process.env;
8
9if (!REDIS_URL) {
10 LOGGER.error('Please make sure that you have a valid Redis URL in your .env file');
11 process.exit(1);
12}
13
14export const client = new Redis(REDIS_URL);
15
16client.on('connect', () => {
17 LOGGER.info('Connected to Redis');
18});
19
20client.on('error', (err) => {
21 LOGGER.error(`Redis error: ${err}`);
22 process.exit(1);
23});
If you aren’t familiar with logging, you can use console.log instead.
cache.ts and implement the caching functionality. 1import mongoose, { Document } from 'mongoose';
2import { client } from '../database/redis';
3
4declare module 'mongoose' {
5 interface Query<ResultType, DocType, THelpers, RawDocType = DocType> {
6 useCache: boolean;
7 hashKey: string;
8 cache(options?: { key?: string }): this;
9 }
10}
11
12const exec: typeof mongoose.Query.prototype.exec = mongoose.Query.prototype.exec;
13
14mongoose.Query.prototype.cache = function (options = {}) {
15 this.useCache = true;
16 this.hashKey = JSON.stringify(options.key || '');
17 return this;
18};
19
20mongoose.Query.prototype.exec = async function <T extends Document>(): Promise<T[]> {
21 if (!this.useCache) {
22 return exec.call(this);
23 }
24
25 const key = JSON.stringify(
26 Object.assign({}, this.getQuery(), {
27 collection: this.model.collection.name,
28 })
29 );
30
31 const cacheValue = await client.hget(this.hashKey, key);
32
33 if (cacheValue) {
34 const doc = JSON.parse(cacheValue);
35 return Array.isArray(doc)
36 ? doc.map((d: any) => new this.model(d))
37 : [new this.model(doc)];
38 }
39
40 const result = await exec.call(this);
41 await client.hset(this.hashKey, key, JSON.stringify(result), 'EX', 1000);
42
43 return result;
44};
45
46export const clearHash = (hashKey: string): void => {
47 client.del(JSON.stringify(hashKey));
48};
Let’s understand the code above.
First, we are extending the mongoose.Query interface to add the cache method.
The cache method is used to enable caching for the query.
The hashKey is used to store the cache data in the Redis server.
The exec method is overridden to implement the caching functionality.
If the useCache is set to true, then we check if the data is available in the cache.
If the data is available in the cache, then we return the data from the cache.
If the data is not available in the cache, then we execute the original exec method and store the data in the cache.
The clearHash method is used to clear the cache data for a specific hashKey.
cleanCache.ts and implement the clean cache middleware.1import { NextFunction, Response } from 'express';
2import { clearHash } from '../services/cache.service';
3
4export default async (req: Request, res: Response, next: NextFunction) => {
5 await next();
6 clearHash((req as any).user.id.toString());
7};
Let’s understand the code above.
The cleanCache middleware is used to clear the cache data after the request is completed.
await next() is used to execute the next middleware then we use the clearHash method to clear the cache data for the specific hashKey.
We are using the clearHash method to clear the cache data for the specific hashKey.