# 9. Redis - Node.js

[Download Redis For Windows(msi file recommeded)](https://github.com/tporadowski/redis/releases)

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).

```javascript
//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

```javascript
npm i redis
```

### Connect Redis in Node.js

```javascript
// 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

```javascript
        //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**
    
    ```javascript
    //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)**
    
    ```javascript
         // 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)**
    
    ```javascript
    // 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)**
    
    ```javascript
         //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
    
    ```javascript
           //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.
        
    
    ```javascript
    // 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.

```javascript
// 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**

```javascript
        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**

```javascript
// 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

```javascript
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();
```
