paint-brush
How to Reduce Response Times and Improve Performance with Redis Cachingby@ljaviertovar
571 reads
571 reads

How to Reduce Response Times and Improve Performance with Redis Caching

by L Javier TovarMay 3rd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Caching is the process of storing copies of data in memory to allow applications to access and retrieve data faster. Redis has become the most popular caching database as it allows the website to dramatically increase performance and work more smoothly by accessing data in a matter of milliseconds. The use of Redis is highly recommended when access speed and response times are critical for a business solution.
featured image - How to Reduce Response Times and Improve Performance with Redis Caching
L Javier Tovar HackerNoon profile picture

We live in the age of immediacy, where time is already worth more than money. And that era, combined with the dynamism of the internet, makes our audience more demanding every day.


The content of your website may be excellent, but if the loading speed is not good, will people wait to see it?


According to several studies, half of the users do not wait more than three seconds. This is a challenge for websites such as e-commerce, where users may abandon the page if it does not load almost immediately and lose potential sales.


Caching your web application data could be crucial to solving this problem and providing high-performance gains as you scale.


In recent years, Redis has become the most popular caching database as it allows the website to dramatically increase performance and work more smoothly by accessing data in a matter of milliseconds.


What is Redis?

The Remote Dictionary Server (Redis) is a high-performance open-source NoSQL database that is mainly used as a caching solution for various types of applications.


Redis is based on a hash table structure where each key has an associated value. Compared to other Key-Value databases, Redis allows the use of more complex and flexible structures that open up several possibilities for different business application needs.


The use of Redis is highly recommended when access speed and response times are critical for a business solution. Its use is also indicated when working with real-time applications, which require data to be quickly accessible to improve response times. Among the most common use cases we can find:


  • Chat and messaging systems
  • Listing of the most recent items
  • Real-time counters and statistics usage
  • Online shopping cart management and administration
  • Storing user sessions within an application
  • Support for caching web pages


What is Caching?

Caching is the process of storing copies of data in memory to allow applications to access and retrieve data faster.


The goal of caching is to speed up data access operations better than a database or remote server could allow. This is especially the case for costly (in time) operations.


For example, if our queries require several operations, such as retrieving data from a database, performing calculations, retrieving additional data from other services, etc., we can use caching.


With this, we process the data only once, store it in a cache and then retrieve it directly from the cache without performing all those costly operations. We will then periodically refresh the cache so that users can see updated information.


Caching With Node.js and Redis

Now we will start with the development of our app what we want to achieve in this tutorial is to perform queries to an external API and measure the response time.


After that, we will implement Redis in our application, storing the result of our queries in the cache. With this, we will be able to compare the response time before Redis and after Redis.

Redis Setup

The first thing for this tutorial is to have Redis installed in our local environment. For this, we will follow these installation guides for each platform.


Install on Mac

Install on Ubuntu

Install on Windows


Node.js Setup

We start our Node.js project with the command to create package.json.


npm init


For our Node.js application, we need to install the following dependencies with the following commands:


npm install express redis axios


  • Express to build a server
  • Redis to connect our application with Redis and perform queries
  • axios to perform REST queries


npm install nodemon response-time -D


  • nodemon to automatically wake up our application after each change
  • response-time to see how long each request takes


In our package.json file, we will create inside the “scripts” key a new script that we will use to raise our application.


"dev": "nodemon src/app.js"


Build the Node.js Application

Set up the initial boilerplate for the Node.js application like this. We create an app.js file, and inside it, we add the following lines:


const express = require('express')
const responseTime = require('response-time')
const redis = require('redis')
const axios = require('axios')const app = express()app.listen(process.env.PORT || 3000, () => {
    console.log("Node server started")
})


Now we have the Express server created if we execute the command.


npm run dev


We will see in the console the message "Node server started"

Retrieving Data From the External API

For our tutorial, we will use the REST version of The Rick and Morty API, which offers us the following endpoints:


{
  "characters": "https://rickandmortyapi.com/api/character",
  "locations": "https://rickandmortyapi.com/api/location",
  "episodes": "https://rickandmortyapi.com/api/episode"
}


Now we will make a request with axios to the /characterendpoint.


const express = require('express')
const responseTime = require('response-time')
const redis = require('redis')
const axios = require('axios')
const app = express()
app.use(responseTime())app.get("/character", async (req, res) => {
  try {
    const response = await axios.get('https://rickandmortyapi.com/api/character')   
   
    await client.set('characters', JSON.stringify(response.data))       
    return res.status(200).json(response.data) 
  } catch (err) {   
    return res.status(err.response.status).json({ mmessage: err.mmessage }) 
  
  }
});
app.listen(process.env.PORT || 3000, () => { console.log("Node server started")})


With this request that we have made, the API returns us an object with all the characters, and what we are interested in seeing is the response-timethat this request has taken.


Using response-time that we have added as a middleware, we will be able to see in the headers of the request a new header called X-Response-Timewhich will indicate the time in more.


This request has taken 238.831ms:

/character

Implement Redis Cache for our Endpoint

Now, let’s see how we can improve the application's performance by caching.

First, we need to connect to the Redis server through our application. We use the installed Redis package for this task.


By default, redis.createClient() will use 127.0.0.1 and 6379 as the hostname and port, respectively. If you have a different host/port, you can supply them like so:


const client = redis.createClient(port, host)
const runApp = async () => {  
  
  const client = redis.createClient()
  
  client.on('error', (err) => console.log('Redis Client Error', err))
  
  await client.connect()
  
  console.log('Redis connected!')
}
runApp()


Now we will make the same request to the API and cache the response with the set() method of the Redis client, which receives as the first parameter the name of the key we want to save. As the second parameter, the value of this key, which we have to save as a string, so we have to parse the JSON to string.


const runApp = async () => {
  app.get('/character', async (req, res) => {
    
    try {
      const response = await axios.get('https://rickandmortyapi.com/api/character')
      await client.set('characters', JSON.stringify(response.data))
    
      return res.status(200).json(response.data)   
    
    } catch (err) {   
      return res.status(err.response.status).json({ mmessage: err.mmessage })  
    }
  })
}
runApp()


Once we reload our app and we get back the result of the query, we will have already cached the answer.


To validate this, we will use Redis-commander. Redis-commander allows us to view through a web interface our Redis database. We install it with the following command:


npm install -g redis-commander


This package will provide us with a redis-commandercommand that, when executed, will execute the database web interface on port 127.0.0.1:8081.

redis-commander


redis-commander



And when we enter redis-commander, we will see that the response to our /characterrequest has already been stored with the name that we have assigned to it.


The process of caching with Redis is quite simple. When we receive a request from the user, we first check if the requested data is already cached. If so, we can quickly retrieve the data from Redis and send the response.


However, if the data is not cached, which we call a cache miss, we must first retrieve the data from the database or external API and send it to the client. We also make sure to store the retrieved data in the cache so that the next time the same request is received, we can simply send the cached data to the user faster.


const runApp = async () => {
  app.get('/character', async (req, res) => {
      
    try {      
      const cacheCharacters = await             client.get('characters')
        
      if (cacheCharacters) {
        return res.json(JSON.parse(cacheCharacters))
      }      
        
      const response = await axios.get('https://rickandmortyapi.com/api/character')      
       await  client.set('characters', JSON.stringify(response.data))
      
      return res.status(200).json(response.data)    
    } catch (err) {      
      return res.status(err.response.status)
             .json({ mmessage: err.mmessage })
    }
  })
}
runApp()


Now that we are retrieving the information from the cache, the response time has dropped considerably to 4.230ms.


/character


It is important to note that we have only touched the surface in this tutorial, and there is much more than Redis has to offer. I highly recommend consulting its official documentation.


Here is the complete script with one more example with an endpoint receiving parameters:


const express = require('express')
const responseTime = require('response-time')
const redis = require('redis')
const axios = require('axios')

const runApp = async () => {

  // connect to redis
  const client = redis.createClient()
  client.on('error', (err) => console.log('Redis Client Error', err));
  await client.connect();
  console.log('Redis connected!')

  
  const app = express()
  // add response-time to requests
  app.use(responseTime())

  app.get('/character', async (req, res) => {

    try {

      // check if the request is already stored in the cache, if so, return the response
      const cacheCharacters = await client.get('characters')
      if (cacheCharacters) {
        return res.json(JSON.parse(cacheCharacters))
      }

      // makes the request to the API
      const response = await axios.get('https://rickandmortyapi.com/api/character')

      /* Another way to save the data is to save it with the name of the requets url, with the property
       req.originalUrl which would be the same as '/character'
       await client.set(req.originalUrl, JSON.stringify(response.data))
      */

      // save the response in the cache
      await client.set('characters', JSON.stringify(response.data))
      return res.status(200).json(response.data)

    } catch (err) {
      return res.status(err.response.status).json({ mmessage: err.mmessage })
    }

  })

  app.get('/characters/:id', async (req, res) => {

    try {

      const cacheCharacter = await client.get('cacheCharacter' + req.params.id)
      if (cacheCharacter) {
        return res.json(JSON.parse(cacheCharacter))
      }

      const response = await axios.get('https://rickandmortyapi.com/api/character/' + req.params.id)

      await client.set('cacheCharacter' + req.params.id, JSON.stringify(response.data))
      return res.json(response.data)

    } catch (err) {
      return res.status(err.response.status)
        .json({ message: err.message })
    }

  })

  app.listen(process.env.PORT || 3000, () => {
    console.log(`server on port 3000`)
  })

}

runApp()


Conclusion

In this tutorial, we have seen a quick introduction to Redis and created a simple cache with it for a Node.js application.


You can now use Redis to cache frequently queried data in your application for a significant performance boost, especially in projects that handle many requests per second, where the performance provided by Redis becomes very important.




Want to Connect? Love connecting with friends all around the world on Twitter.


Also published here.