Caching Nodejs Api Responses using Redis

GitHub Repository Link

Install Redis

JSONPlaceholder

» What we building?

Hello everyone! In this blog, we will look at how we can create a caching layer with Redis in a Node.js application and then we will test that in Postman.

So, let’s start.

» Initialize the Project?

Open the terminal and create a new directory name it nodejs-redis-cache and then navigate to the project directory and run the command: npm init -y to create this project as a node.js.

mkdir nodejs-redis-cache
cd nodejs-redis-cache
npm init -y

» Install redis

Follow the documentation provided on Redis website for downloading it on windows/linux.

For Mac: Install homebrew and then to install Redis run the below command in the terminal:

brew install redis

Start redis server:

redis-server

To stop the Redis server click: CTRL + C

» Express server

Open the project into the VS code and create two files index.js and a .env file and now we install all the dependencies

In terminal run the command:

npm i axios express nodemon redis dotenv

And then let’s open the index.js file and in here we start by importing the necessary libraries that we will need.

const express = require('express'); 
const app = express();
app.use(express.json());
const axios = require('axios');
require('dotenv').config();
const { createClient } = require("redis");

We import express for creating our server, and then we create an instance of our express application.

Then we have Middleware to parse incoming requests with JSON payloads.

We also bring in axios for making HTTP requests, and redis for interacting with our Redis database.

Then we load environment variables from .env file using dotenv package

Then we bring in createClient from redis to connect to the redis server

» Redis client

Next we will initialise a redis client.

// index.js
const initializeRedisClient = async function() {
  let redisURL = process.env.REDIS_URI;
  if (redisURL) {
    redisClient = createClient({ url: redisURL }).on("error", (e) => {
      console.error(`Failed to create the Redis client with error:`);
      console.error(e);
    });

    try {
      await redisClient.connect();
      console.log(`Connected to Redis successfully!`);
    } catch (e) {
      console.error(`Connection to Redis failed with error:`);
      console.error(e);
    }
  }
}

We are going to define an asynchronous function called initializeRedisClient. In this function we will set up and connect to the Redis server.

Redis sever by default starts on port 637

So in the .env file, we create a variable named REDIS_URI=“redis://localhost:6379” and we say the server will run on port 637

<!-- .env file -->
REDIS_URI="redis://localhost:6379"

Now back to the index.js file we create a variable named redisUrl which is going to bring in the redis server URL present in .env file

And after this we check If a Redis server URL is provided (i.e., if REDIS_URI is set in the environment variables), and if it is we proceed with creating the Redis client.

So to do that we create a Redis client using the createClient method provided by the redis.

And after it We pass the Redis server URL (redisURL) as a parameter to the createClient function.

We attempt to establish a connection to the Redis server using redisClient.connect(). If the connection is successful, we log a success message. If an error occurs during the connection process, we catch the error and log it.

This REDIS client will be used for caching data retrieved from an external API(JSONPlaceholder).

» CACHE Object

Next, we have a cache object. This object has three methods: get, set, and del, which are used to get, set, and delete data from our Redis cache, respectively. We’re using the redisClient object to interact with the Redis database.

// index.js
const cache = {
  get: async (key) => {
    const value = await redisClient.get(key);
    return value ? JSON.parse(value) : null; 
  },
  set: async (key, value, expiry = 60) => { 
    await redisClient.set(key, JSON.stringify(value), 'EX', expiry);  
  },
  del: async (key) => {
    await redisClient.del(key);
  }
};

The get method is going to retrieve the data from the cache based on the provided key and it will use the redis client.get key to fetch the data associated with the given key from the redis cache. and if the data exist for the provided key we return it after parsing it from JSON format using JSON.parse() and if there is no data for the key we are going to return null.

The set method sets data in the cache and this set method is going to take three parameters Key, value and expiry for which we set a default value tof 60 second. it is then going to use the redisClient.set method which is going to take four parameters key, value, ‘EX’, expiry to set the provided value in the Redis cache with the given key. and before we store the value it is converted to a JSON string using JSO.stringify. EX is for expiry time in seconds

This del method deletes data from the cache based on the provided key. It uses redisClient.del(key) to delete the data that’s associated with the given key from redis cache.

This cache object is used to interact with the Redis cache in order to store, retrieve, and delete data.

» Create Express Server

Now, we will setup the express server. For that we will have the asynchronous function initializeExpressServer. This function first calls initializeRedisClient to connect to the Redis server before we can continue.

Then, it sets up an Express server with a single GET route at /comments."

// index.js
const initializeExpressServer = async function() {
  await initializeRedisClient();

  app.get(
    "/comments",
    async (req, res) => {
      try {
        let data = await cache.get('comments');
        if (!data) {
          const response = await axios.get(
            "https://jsonplaceholder.typicode.com/comments"
          );
          data = response.data;
          await cache.set('comments', data);
        }
        res.send(data);
      } catch (error) {
        console.error(error);
        res.status(500).send({ error: "Error fetching data from API" });
      }
    }
  );

  const port = 3000;
  app.listen(port, () => {
    console.log(`Server is running on http://localhost:${port}`);
  });
}


initializeExpressServer()
  .then()
  .catch((e) => console.error(e));

Then we will define a route using app.get() method. This route listens for GET requests to the “/comments” endpoint. When a request is made to this endpoint, the callback function is executed.

Inside this callback function, we will retrieve cached data using cache.get('comments').

And If in cache we have a cached data, we will return that; otherwise, we will make a request to the external API which is jsonplaceholeder to fetch the comments.

Now instead of commnets you can fetch any data that you want like users, posts anything, so head to the typicode website and here you will find the endpoints, they provide. So grab that you want and paste it as your endpoint.

If the data is not cached, we make a GET request to “https://jsonplaceholder.typicode.com/comments" using Axios. Once we receive the response, we extract the data from the response object and store it in the data variable. We then cache this data using cache.set('comments', data).

Finally, we send the data as the response to the client.

After we have defined the route, we need to specify the port on which our Express server will listen for incoming requests. For it we call the listen() method on our app object, which in turn will starts the server.

Then, we call the initializeExpressServer() function to start our Express server. If an error occurs during the server initialization process, it is caught and logged to the console.

Add a script to package.json file:

"scripts": {
  "start": "nodemon index.js",
}

Start the Server:

npm start

Test on Postman at: http://localhost:3000/comments

And look at how the time reduces when we fetch the data for the second time.


And that’s it!

👉 I hope this helped you. And I’ll see you in next blog till then bye bye.