9. Redis - Node.js
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
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 indiaLists(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' ]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' ]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)//2Hases
//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
Publisher client connects.
Subscriber client connects and subscribes to
"dummy-channel".Publisher sends 2 messages β Subscriber instantly receives them.
After 1 second delay β subscriber unsubscribes and quits.
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();