Implement caching in NodeJS and improve (TTFB) by 20 times.

Node JS offers to build fast and scalable applications. However, sometimes an API built has to do some heavy computation or fetch data from a distant server. Which affects the response time (TTFB) of the API very badly.

Therefore, If your app returns the same data for some particular requests by different clients then you can cache the response of such endpoints by using middleware.

1. Install the node-cahce NPM package

To install this package, use the following command.

npm install --save node-cache

2. Import the package and Initialize it

Add the following lines in your “server.js” or “index.js” file. This is the file where you’ve initialized the ExpressJs app instance.

const NodeCache = require( "node-cache" );
const myCache = new NodeCache( { stdTTL: 300, checkperiod: 310 } );

In the second line, we’ve initialized stdTTL to 300 seconds which means the cache will be valid for 5 minutes.

3. Create middleware for the routes for which you want to implement caching

Following is an example of an empty middleware function.

app.use(function (req, res, next) {
  
});

4. Add the caching code in the middleware

In this middleware, First, we’ll check if the request method is “GET” or not because our objective is to only cache the GET requests.

So if the request method is not “GET” then we’ll call the “next()” method followed by the “return” keyword.

But if the request method is “GET”, then we’ll check if we have any cache is saved for the current requested URL. If yes then we’ll return the response from the cache and add a header “X-Proxy-Cache” with the value “HIT”. Otherwise, we’ll overwrite the “send” method of the request object with a function that will save the cache for headers and the body of the current request. After saving cache, we’ll add a header “X-Proxy-Cache” with the value “MISS” and then we’ll call the original send method of the request object, and then finally we’ll call the “next()” method followed by the “return” keyword.

Flow diagram for caching middleware

After adding all of this code our middleware will look like the following middleware.

app.use(function (req, res, next) {
  if (req.method != 'GET') {
    return next();
  }
  var cachedReponse = myCache.get(req.url);
  if (cachedReponse) {
    res.header(cachedReponse.headers);
    res.header('X-Proxy-Cache', 'HIT');
    return res.send(cachedReponse.body);
  } else {
    res.originalSend = res.send;
    res.send = (body) => {
      myCache.set(req.url, {
        'headers'   : res.getHeaders(),
        'body'      : body
      });
      res.header('X-Proxy-Cache', 'MISS');
      res.originalSend(body);
    };
    return next();
  }
});

Conclusion

Now all the routes after this middleware will be cached for 5 minutes.

Here is an example of a full server.js file

const express = require('express');
const request = require('request');
const NodeCache = require( "node-cache" );
const myCache = new NodeCache( { stdTTL: 300, checkperiod: 310 } );
const app = express();
const port = 8000;

app.get('/greetings', function(req, res, next) { // This route won't be cached
  res.send('Helloworld!');
});

// ======================================== Caching Middleware ===========================
app.use(function (req, res, next) {
  if (req.method != 'GET') {
    return next();
  }
  var cachedReponse = myCache.get(req.url);
  if (cachedReponse) {
    res.header(cachedReponse.headers);
    res.header('X-Proxy-Cache', 'HIT');
    return res.send(cachedReponse.body);
  } else {
    res.originalSend = res.send;
    res.send = (body) => {
      myCache.set(req.url, {
        'headers'   : res.getHeaders(),
        'body'      : body
      });
      res.header('X-Proxy-Cache', 'MISS');
      res.originalSend(body);
    };
    return next();
  }
});
// =================== All the routes after this middleware will be cached ================

app.get('/todos/:todo_id?', (req, res) => { // This route will be cached
  var request = require('request');
  var apiUrl = 'https://jsonplaceholder.typicode.com/todos' + (req.params.todo_id ? '/'+req.params.todo_id : '');
  request(apiUrl, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      res.send(body);
    }
  });
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

Note: If you have any trouble understanding anything from this article feel free to drop a comment. I’ll be happy to help.

Leave a Reply

Your email address will not be published. Required fields are marked *