Skip to main content

Command Palette

Search for a command to run...

9. Redis - Node.js

Published
β€’7 min read

Download Redis For Windows(msi file recommeded)

Setup Redis In Windows - Copy path of redis from Program files(in C drive) . Now Paste that path in env variable(user acc section in control panel).

//in cmd -> check redis server
user>redis-server
[6204] 13 Sep 22:40:37.879 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
[6204] 13 Sep 22:40:37.879 # Redis version=5.0.14.1, bits=64, commit=ec77f72d, modified=0, pid=6204, just started
[6204] 13 Sep 22:40:37.879 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
[6204] 13 Sep 22:40:37.882 # Could not create server TCP listening socket *:6379: bind: An operation was attempted on something that is not a socket.

What is Redis ? Why we use this ?

Redis (Remote Dictionary Server) is an open-source, in-memory data structure that store data in the form of key-value pairs that can be used as (similar to a JavaScript Map or Python dict), but much faster since everything is kept in RAM.

Redis can be used as a:

  • Database

  • Cache

  • Message broker

  • Streaming engine

Redis solves performance and scalability problems.

Caching β†’ Frequently accessed data (like user sessions, product details, leaderboard scores) is stored in Redis instead of querying(fetch) the main DB (MySQL, MongoDB) every time.

  • βœ… Reduces database load

  • βœ… Makes responses super fast

Session Management β†’ In web apps, user sessions can be stored in Redis (instead of in-memory in Express or in DB). This helps with:

  • Scalability (sessions available across multiple servers)

  • Persistence (sessions don’t vanish if one server crashes)

Installation

npm i redis

Connect Redis in Node.js

// In server.js
const redis = require('redis')
const client = redis.createClient({
    host:'localhost', //redis server now hosted on local host
    port:6379
})
//event listener
client.on('error', (error)=>{
    console.log("Redis client error occurred", error)
})
async function testRedisConnection(){
    try{
        await client.connect();
        console.log("Redis Server Connected !")
    }
    catch(err){
        console.log(err)
    }
    finally{
        await client.quit() // avoid leaving open connection
    }
}
testRedisConnection()

Basic Redis commands in Node.js

        //set and get
        await client.set("name","ayush")
        const extractValue = await client.get("name");
        console.log(extractValue)

        //delete
        const deleteCount = await client.del("name");
        console.log(deleteCount)
        const updatedValue = await client.get("name");
        console.log(updatedValue)

        //incremtn and decrement
        await client.set("count", "100");
        const incrementCount = await client.incr("count")
        console.log(incrementCount)

        const decrementCount = await client.decr("count")
        console.log(decrementCount)
        await client.decr("count")
        await client.decr("count")
        await client.decr("count")
        await client.decr("count")
        console.log(await client.decr("count"))

// output
ayush
1
null
101
100
95

Data Structures in Redis

  1. String

     //1. String -> set , get , mSet , mGet
     await client.set("user:name","Ayush Rajput")
     console.log(await client.get("user:name"))
    
     await client.mSet(["user:email" , "rajptayus@gmail.com" , "user:age" , "60" , "user:country", "india"]) //["1stkey:name" , "1value" , "2ndkey:age", "2ndvalue"]
     const [email , age , country] = await client.mGet(["user:email", "user:age", "user:country"])
     console.log(email , age, country)
     // Output
     Ayush Rajput
     rajptayus@gmail.com 60 india
    
  2. Lists(like arrays)

          // 2. Lists -> lPush (push at the begining) , rPush(push at the end) ,lRange (retrive element from specific range) ,
          //    lPop(pop and return first eleement) , rPop     
             await client.lPush("notes" , ["note 1" , "note 2" , "note 3"])
             const extractAllNotes = await client.lRange("notes" , 0 , -1) // 0,-1 means extract all 
             console.log(extractAllNotes) //[ 'note 3', 'note 2', 'note 1' ]
    
             const firstNote = await client.lPop("notes")  // it give first elent
             console.log(firstNote) // note 3
    
             const remainingNote = await client.lRange("notes", 0, -1);
             console.log(remainingNote) // [ 'note 2', 'note 1' ]
    
  3. Sets (unique items)

     // 3. Sets -> sAdd (add one or more memeber to set) , sMembers (always return all memebers/elements of sets), sIsMemeber (check wheather particular member present in set or not) , sRem(remove one or more item from set)
     await client.sAdd("user:NickName" , ["name 1" , "name 2" , "name 3"])
     console.log(await client.sMembers("user:NickName")) //[ 'name 3', 'name 2', 'name 1' ]
    
     const isName2IsPresent = await client.sIsMember("user:NickName" , "name 1")
     console.log(isName2IsPresent) // 1 (means true)
    
     await client.sRem("user:NickName" ,'name 2')
     console.log(await client.sMembers("user:NickName")) //[ 'name 3', 'name 1' ]
    
  4. Sorted Sets (leaderboards, ranking)

          //4. Sorted Sets-> zAdd , zRange , zRem
             await client.zAdd('cart', [
                 {
                     score:100 ,
                     value: 'Cart 1'
                 },
                 {
                     score: 150 ,
                     value:'Cart 2'
                 },
                 {
                     score:10 , 
                     value:'Cart 3'
                 }
             ])
             const getTopCartItems = await client.zRange("cart", 0 , -1); // got cart in descending order of score
             console.log(getTopCartItems) //[ 'Cart 3', 'Cart 1', 'Cart 2' ]
    
             const extractAllCartItemsWithScores = await client.zRangeWithScores("cart",0,-1)
             console.log(extractAllCartItemsWithScores) //[
                                                       //{ value: 'Cart 3', score: 10 },
                                                      //  { value: 'Cart 1', score: 100 },
                                                      // { value: 'Cart 2', score: 150 }
                                                       //]
    
             const cartTwoRank = await client.zRank("cart", "Cart 2")
             console.log(cartTwoRank)//2
    
  5. Hases

            //5. Hashes , hSet , hGet , hGetAll , hDel
             await client.hSet('product:1', {
                 name:'Product 1',
                 description:'product 1 description',
                 rating:5
             })
             const getProductRating = await client.hGet('product:1','rating');
             console.log(getProductRating) //5
    
             console.log(await client.hGetAll('product:1')); //
             console.log(await client.hDel('product:1','rating')) //1 (true)
    

Pub/Sub or Publish/Subscribe (real-time notifications, chat, microservices)

  • Publisher sends a message to a channel (doesn’t care who listens).

  • Subscribers listen to channels and react when messages are published.

  • Redis works as a message broker here.

  • Why: To allow real-time communication between processes/services (chat apps, notifications, live updates).

    Example:

    • A news app (Publisher) sends headlines to a "news" channel.

    • Many users (Subscribers) subscribe to "news" channel and get updates instantly.

    // Import the redis library
    const redis = require('redis')

    // Create a Redis client (this will be used as Publisher)
    const client = redis.createClient({
        host: 'localhost', // Redis server is running locally
        port: 6379         // Default Redis port
    })

    // Listen for errors (always good practice)
    client.on('error', (error) => {
        console.log("Redis client error occurred", error)
    })

    async function testAdditonalDetail () {
        try {
            // πŸ”Ή Connect the client to Redis (this acts as the Publisher)
            await client.connect(); 

            // πŸ”Ή Create a duplicate client for Subscriber
            // Why duplicate? Because in Redis, one client can't both subscribe AND publish simultaneously.
            const subscriber = await client.duplicate() 

            // Connect the subscriber client to Redis
            await subscriber.connect() 

            // πŸ”Ή Subscribe to a channel called 'dummy-channel'
            // Every time a message is published to this channel, the callback runs.
            await subscriber.subscribe('dummy-channel', (message, channel) => {
                console.log(`Received message from ${channel}: ${message}`)
            })

            // πŸ”Ή Publish messages to the channel
            await client.publish('dummy-channel', "Some dummy data from publisher")
            await client.publish('dummy-channel', "Again Some dummy data from publisher")

            // πŸ”Ή Small delay (so subscriber has time to consume messages before we quit)
            await new Promise((resolve) => setTimeout(resolve, 1000));

            // πŸ”Ή Unsubscribe from the channel
            await subscriber.unsubscribe('dummy-channel')

            // Close subscriber connection
            await subscriber.quit()

        } catch (err) {
            console.log(err)

        } finally {
            // Always quit publisher client at the end
            await client.quit();
        }
    }
    // Run the function
    testAdditonalDetail();

πŸ”Ή Flow of Execution

  1. Publisher client connects.

  2. Subscriber client connects and subscribes to "dummy-channel".

  3. Publisher sends 2 messages β†’ Subscriber instantly receives them.

  4. After 1 second delay β†’ subscriber unsubscribes and quits.

  5. Publisher also quits.

PIPELINING & Transaction- In REDIS

Pipelining is a technique where a process is broken into a sequence of smaller stages, and multiple stages are executed in parallel. Instead of finishing one task fully before starting the next, you overlap tasks so the system works on different parts of multiple tasks at the same time.

Redis pipelining is a technique that allow you to send multiple commands to the redis server in one go without waitng for each other response or we can say that redis pipelining is a techniuqe of batching multiple commands into a single request

  • Instead of request β†’ response β†’ request β†’ response,

  • You do request β†’ request β†’ request β†’ ... β†’ response batch.

This reduces network round trips and improves performance dramatically.

Redis transactions(similar kinda pipelining) let you execute a group of commands in a single step.
It ensures atomic execution(all commands run together without other commands in between) of the queued commands.

// WITHOUT PIPELINING
Client: SET user:1 "Ayush"
Server: OK

Client: GET user:1
Server: "Ayush"

Client: INCR counter
Server: (integer) 1

// WITH PIPELINING
Client: 
  SET user:1 "Ayush"
  GET user:1
  INCR counter

Server (sends all responses at once):
  OK
  "Ayush"
  (integer) 1

Node.js Pipelining Code

        const pipeline =  client.multi();
       pipeline.set("user:1","Ayuhs")
        pipeline.set("user:2","Malika")
        pipeline.get("user:1")
      pipeline.get("user:2")

        const result = await pipeline.exec();
        console.log(result)
output: [ 'OK', 'OK', 'Ayuhs', 'Malika' ]

//With looops
     const pipeLineOne = client.multi();
        for(let i=0; i<1000; i++){
            pipeLineOne.set(`user:${i}:action` , `Action:${i}`)
        }

      console.log(await pipeLineOne.exec())

Node.js Transaction code

// Start a transaction
const bro = client.multi();

// Queue commands (not executed yet)
bro.decrBy('account:1234:balance', 100);  // deduct 100
bro.incrBy('account:0000:balance', 100);  // add 100

// Execute transaction atomically
const result = await bro.exec();

// NOTE:
// - Atomicity = all commands run together without other commands in between.
// - BUT if one command fails at runtime, Redis does NOT rollback automatically.
// - Example: If 'account:1234:balance' contains a string, DECRBY fails, but INCRBY still executes.

IORedis

Similar to redis but give some more features than redis , this will allow us to use automatic pipeline(cover in next article of microservices) and other data structres will be same here

const Redis = require('ioredis') // redis client lobrary for node.js

const redis = new Redis(); //create client

async function redisIoDemo(){
    try{
        await redis.set("key1","value1")
        await redis.set("key2","value2")
        console.log(await redis.get("key1"))

    }
    catch(err){
        console.log(err)
    }
    finally{
        redis.quit()
    }
}
redisIoDemo();