Implement Redis caching with ExpressJS

In this article, we are going to create a simple ExpressJS project which will call a starwars API to fetch its movie character details. This API is provided by Swapi . You can find the code for this at Github .

Installing Redis

Redis is an open-source, key-value database. It is widely used for caching as an in-memory data structure. It can also be used as a persistence database.

Windows

On windows, you can install Redis by downloading it from github.com/dmajkic/redis/downloads after unzip run the executable file.

Mac

On Mac, you can use a Homebrew package manager.

If homebrew is not already installed you can install it by running the following command

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once Homebrew is installed you can install Redis

brew install redis

Docker

The best way to get Redis running quickly on any operating system is using Docker.

Once docker is successfully installed you can execute the following command to run Redis in the background.

docker run --name some-redis -d redis

Setting up our project

Let's start by creating our project directory and cd into it.

mkdir starwars-redis-express
cd starwars-redis-express

Initialize a NodeJS project passing the -y flag which accepts all the defaults.

npm init -y

For this project, we will use ExpressJS as our web framework, Node Fetch library to call starwars API, and Redis library to communicate with Redis.

Install express, node-fetch, and Redis

npm i express node-fetch redis

Install nodemon as a dev dependency so that we don’t have to manually restart the server again and again after every change.

npm i -D nodemon

Open package.json and add a script like this

"scripts": {
  "start": "nodemon index"
}

Now let's create a simple express server.

Create index.js file

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/startwars/people', (req, res) => {
  res.send('Star Wars');
});
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
});

Now let's run the server by executing the command npm run start.

If you visit http://localhost:3000/starwars/people you should see a text Star Wars on your browser.

The endpoint is startwars because we are going to consume Star Wars API from swapi.co in this example app.

Let’s call the star wars actual API to fetch the results and send them to the client.

First import the nodefetch libaray

const fetch = require('node-fetch')

create an async function to call startwars API.

const getPeople = async (req, res, next) => {
  try {
    const { id } = req.params;
    console.log(`Fetching people data for id ${id}`);
    const response = await fetch(
      `https://swapi.co/api/people/${req.params.id}`
    );
    const people = await response.json();
    res.send(getResponse(people));
  } catch (err) {
    console.error(err);
    res.status(500);
  }
};

Implement the getResponse function use above

const getResponse = (people) => {
  const { name, height, gender } = people;
  return `
    <ul>
      <li>Name: ${name}</li>
      <li>Height: ${height}</li>
      <li>Gender: ${gender}</li>
    </ul>
  `;
}

Change our API definition to include id as the URL parameter

app.get('/startwars/people/:id', getPeople);

Above API calls the getPeople function which then calls the starwars API to fetch person data by id.

Now if you visit URL http://localhost:3000/startwars/people/1 you should get a below response

1_-C4T8P8VC61j400ghh5Fbg.png

If you check the network logs in the browser our API call approximately take 2 sec to respond.

1_eIUTHnuqZ3HVP0HTZy0FEw.png

Now let's add caching to our API to make it faster

Import the Redis package

const redis = require('redis');

Connect to Redis client

const REDIS_PORT = process.env.REDIS_PORT || 6379;
const client = redis.createClient(REDIS_PORT);

Create a middleware function to return the cached response if available

const cachePeople = (req, res, next) => {
  const { id } = req.params;
  client.get(id, (err, data) => {
    if (err) throw err;
    if (data !== null) {
      res.send(getResponse(JSON.parse(data)));
    } else {
      next();
    }
  });
};

This function checks if there is an entry against the id in Redis. If it finds one it will return the cached result other will return next() which will allow continuing with the normal API call.

Change the API call line to make use of the above middleware.

app.get('/startwars/people/:id', cachePeople, getPeople);

Last we need to add a line in our getPeople function right before we send the response to the client to set an entry in Redis after fetching result from starwars API.

client.set(id, JSON.stringify(people), 'EX', 3600);
res.send(getResponse(people));

This will create a cache entry with key id for 3600 sec.

Now if you visit the URL http://localhost:3000/startwars/people/1 first time it will take around ~2sec to load if you refresh it you will see response time will be less than ~10ms

1_JITCfYFxOG3d-t00q1_gJQ.png

If you change the id from 1 to 2 again the first response time will be high but further requests will be served from the cache until the cache entry expires.

That’s it, you check out the code on GitHub.

This article is inspired by Brad’s video tutorial youtube.com/watch?v=oaJq1mQ3dFI. Follow it to learn a lot of cool things.

Thanks