Skip to main content

Redis Cache with Nodejs, AWS

Greetings

Modern applications are highly data-driven, and distributed and performance is a top priority. Redis has become one of those critical components in most applications. In this article, we will briefly look into Redis and how we can use it in a NodeJS application.

"Power real-time applications with sub-millisecond latency"

Redis

Fast, open source in-memory, a key-value data store for use as a database, cache, message broker, and queue (from the doc). It stands for Remote Dictionary Server.

We usually think of Redis as a cache because it is the most used use case. But Redis is more than a cache. As an example, Redis is an ideal option to store user sessions in web applications. Here are a few use cases but for more details better to read the documentation.
  • Caching
  • Session store
  • Messaging Queue
  • Gaming leader boards
  • Geospatial
Caching is a must in Microservices architecture to improve performance. We utilize Redis for this purpose in our e-commerce application. It is not just for microservices, for example, StackOverflow uses a Redis cluster to improve its performance in its monolith.

Amazon ElastiCache for Redis

Amazon ElastiCache for Redis is a blazing fast in-memory data store that provides sub-millisecond latency to power internet-scale real-time applications (from the doc).

Note that AWS ElastiCache securely runs within AWS hence not accessible from outside. Because of that, I'm using a Docker container for the demonstration. The good thing is ElastiCache Redis is still Redis hence there is no difference. We can use the Docker container for the development environment and AWS in production.
docker run -p 127.0.0.1:6379:6379 --name myredis redis

Benefits

Redis is not the only Caching solution out there. AWS ElastiCache also provides Memcache as a cache. However, Redis comes with extra benefits. On top of that, ElastiCache gives us more freedom as it is managed by AWS.
  • Extreme performance
  • Fully managed
  • Secure
  • Highly available and reliable
  • Scalable
  • Flexible data structures - Strings, Lists, Sets, Geospatial, JSON, etc
  • Easy to use

ioredis

In this exercise, I'm using ioredis which is a robust, full-featured Redis client for NodeJS.
Note that NodeJS is not the only option. It supports multiple languages like Java and Python.

Project setup

Let's quickly set up a project for the demonstration. In a production project, you will have more options but you get the idea here.
mkdir aws-redis-nodejs && cd aws-redis-nodejs
npm init -y
npm install express cors ioredis
mkdir src
touch src/index.js
touch src/redis-gateway.js
touch src/movie-service.js

package.json

{
  "name": "aws-redis-nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "ioredis": "^5.3.1"
  }
}
To simulate a slow backend service/ network call we can use a timeout.

movie-service.js

const findMovie = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        id: 123,
        title: "The Godfather",
      });
    }, 2000);
  });
};

export default findMovie;

Redis connection

Let's create functions to connect and use Redis. As mentioned above, operations are very easy to use.

redis-gateway.js

import Redis from "ioredis";

const redis = new Redis({
  host: '127.0.0.1',
  port: 6379
});

redis.on('connect', () => console.log('Redis client is initiating a connection to the server.'));
redis.on('ready', () => console.log('Redis client successfully initiated connection to the server.'));
redis.on('reconnecting', () => console.log('Redis client is trying to reconnect to the server...'));
redis.on('error', (err) => console.log('Redis error', err));

export default redis;
After that, we can just call ioredis provided get, set methods.
redis.set('key', 'value');
redis.get('key');
We can provide cache timeout as well. Note that EX for second PX for milliseconds.
redis.set('key', 100, 'ex', 10);
As for this demonstration, I'm using this directly in the server but in production code, you need to apply proper layering.

index.js

import express from "express";
import cors from "cors";

import redis from "./redis-gateway.js";
import findMovie from "./movie-service.js";

const app = express();

app.use(cors());
app.use(express.json());

app.get("/", (req, res) => res.json({ message: "Hello World" }));

app.get("/movies", async (req, res) => {
  let movie = await redis.get("mymovie");
  if (!movie) {
    movie = await findMovie();
    redis.set("mymovie", JSON.stringify(movie), "EX", 60);
  } else {
    movie = JSON.parse(movie);
  }
  res.json(movie);
});

app.listen(3000, () => console.log("app listening on port 3000"));

Demo

Now let's invoke our endpoint. Ideally, this should take slightly more than 2 seconds due to our simulation.
curl localhost:3000/movies
Run it again and enjoy the performance improvement as now the data is loaded from the Redis cache.

References

https://aws.amazon.com/redis/
https://aws.amazon.com/elasticache/redis/
https://www.npmjs.com/package/ioredis
https://docs.redis.com/latest/rs/references/client_references/client_ioredis/
https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Endpoints.html

Comments