Node.js with Redis
Node.js and Redis are a natural fit. This lesson covers using Redis with Node.js.
Redis Node.js Clients
Mainstream Node.js Redis clients:
- ioredis: feature-rich, supports cluster, sentinel, Lua scripts
- redis (node-redis): official client, simple to use
- redis (v3): older version, widely used
💡 Recommendation: New projects should use ioredis or node-redis v4+.
ioredis
Installation
BASH
npm install ioredis
Basic Connection
JAVASCRIPT
const Redis = require('ioredis');
// Connect to Redis
const redis = new Redis({
host: 'localhost',
port: 6379,
// password: 'your_password',
db: 0
});
// Test connection
redis.ping().then(result => {
console.log(result); // 'PONG'
});
String Operations
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
// SET and GET
async function stringDemo() {
// SET
await redis.set('name', 'Alice');
// GET
const name = await redis.get('name');
console.log(name); // 'Alice'
// SET with expiration
await redis.set('session', 'data', 'EX', 3600); // Expires after 1 hour
// MSET and MGET
await redis.mset('key1', 'value1', 'key2', 'value2');
const values = await redis.mget('key1', 'key2');
console.log(values); // ['value1', 'value2']
// INCR
await redis.set('counter', 0);
const count = await redis.incr('counter');
console.log(count); // 1
// DEL
await redis.del('name');
}
stringDemo();
Hash Operations
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function hashDemo() {
// HSET and HGET
await redis.hset('user:1', 'name', 'Alice');
await redis.hset('user:1', 'age', 25);
const name = await redis.hget('user:1', 'name');
console.log(name); // 'Alice'
// HMSET
await redis.hmset('user:2', {
name: 'Bob',
age: 30,
city: 'Beijing'
});
// HGETALL
const user = await redis.hgetall('user:2');
console.log(user); // { name: 'Bob', age: '30', city: 'Beijing' }
// HKEYS and HVALS
const keys = await redis.hkeys('user:2');
const values = await redis.hvals('user:2');
// HDEL
await redis.hdel('user:2', 'city');
// HINCRBY
await redis.hincrby('user:2', 'age', 1);
}
hashDemo();
List Operations
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function listDemo() {
// LPUSH and RPUSH
await redis.lpush('mylist', 'value1', 'value2');
await redis.rpush('mylist', 'value3');
// LRANGE
const list = await redis.lrange('mylist', 0, -1);
console.log(list); // ['value2', 'value1', 'value3']
// LPOP and RPOP
const left = await redis.lpop('mylist');
const right = await redis.rpop('mylist');
// LLEN
const length = await redis.llen('mylist');
}
listDemo();
Set Operations
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function setDemo() {
// SADD
await redis.sadd('myset', 'a', 'b', 'c');
// SMEMBERS
const members = await redis.smembers('myset');
console.log(members); // Set { 'a', 'b', 'c' }
// SISMEMBER
const exists = await redis.sismember('myset', 'a');
console.log(exists); // 1 (true)
// SREM
await redis.srem('myset', 'a');
// SINTER, SUNION, SDIFF
await redis.sadd('set1', 'a', 'b', 'c');
await redis.sadd('set2', 'b', 'c', 'd');
const inter = await redis.sinter('set1', 'set2');
console.log(inter); // Set { 'b', 'c' }
}
setDemo();
Sorted Set Operations
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function zsetDemo() {
// ZADD
await redis.zadd('leaderboard', 100, 'player1', 200, 'player2', 150, 'player3');
// ZRANGE
const top = await redis.zrange('leaderboard', 0, -1, 'WITHSCORES');
console.log(top); // ['player1', '100', 'player3', '150', 'player2', '200']
// ZREVRANGE (high to low)
const topDesc = await redis.zrevrange('leaderboard', 0, 2, 'WITHSCORES');
// ZSCORE
const score = await redis.zscore('leaderboard', 'player1');
console.log(score); // '100'
// ZINCRBY
await redis.zincrby('leaderboard', 50, 'player1');
// ZREM
await redis.zrem('leaderboard', 'player1');
}
zsetDemo();
Pub/Sub
JAVASCRIPT
const Redis = require('ioredis');
// Subscriber
const subscriber = new Redis();
subscriber.subscribe('news', 'sports');
subscriber.on('message', (channel, message) => {
console.log(`Channel: ${channel}, Message: ${message}`);
});
// Publisher
const publisher = new Redis();
setInterval(() => {
publisher.publish('news', `News message ${Date.now()}`);
}, 1000);
Pipelining
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function pipelineDemo() {
const pipeline = redis.pipeline();
pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.set('key3', 'value3');
pipeline.get('key1');
const results = await pipeline.exec();
console.log(results);
// [ [ null, 'OK' ], [ null, 'OK' ], [ null, 'OK' ], [ null, 'value1' ] ]
}
pipelineDemo();
Transactions
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function transactionDemo() {
const multi = redis.multi();
multi.set('key1', 'value1');
multi.set('key2', 'value2');
multi.incr('counter');
const results = await multi.exec();
console.log(results);
}
transactionDemo();
Connection Management
ioredis has built-in connection pooling — no extra configuration needed:
JAVASCRIPT
const Redis = require('ioredis');
// Single connection (automatic pool)
const redis = new Redis({
host: 'localhost',
port: 6379,
maxRetriesPerRequest: 3,
enableReadyCheck: true
});
// Cluster mode
const cluster = new Redis.Cluster([
{ host: '127.0.0.1', port: 7000 },
{ host: '127.0.0.1', port: 7001 },
{ host: '127.0.0.1', port: 7002 }
]);
node-redis (Official Client)
Installation
BASH
npm install redis
Basic Usage
JAVASCRIPT
const { createClient } = require('redis');
async function main() {
// Create client
const client = createClient({
url: 'redis://localhost:6379'
});
// Connect
await client.connect();
// SET and GET
await client.set('key', 'value');
const value = await client.get('key');
console.log(value); // 'value'
// Hash operations
await client.hSet('user:1', 'name', 'Alice');
const name = await client.hGet('user:1', 'name');
// List operations
await client.lPush('mylist', 'value1');
const list = await client.lRange('mylist', 0, -1);
// Set operations
await client.sAdd('myset', 'a', 'b', 'c');
const members = await client.sMembers('myset');
// Close connection
await client.quit();
}
main();
Pub/Sub
JAVASCRIPT
const { createClient } = require('redis');
async function pubsub() {
const subscriber = createClient();
await subscriber.connect();
// Subscribe
await subscriber.subscribe('news', (message) => {
console.log(`Received: ${message}`);
});
const publisher = createClient();
await publisher.connect();
// Publish
await publisher.publish('news', 'Hello World');
}
pubsub();
Practical Examples
Example 1: Cache Decorator
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
function cache(expire = 3600) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args) {
const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;
// Try to get from cache
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// Execute original method
const result = await originalMethod.apply(this, args);
// Store in cache
await redis.set(cacheKey, JSON.stringify(result), 'EX', expire);
return result;
};
return descriptor;
};
}
class UserService {
@cache(300)
async getUser(userId) {
console.log(`Querying database: userId=${userId}`);
return { id: userId, name: `User${userId}` };
}
}
Example 2: Distributed Lock
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
const { v4: uuidv4 } = require('uuid');
class DistributedLock {
constructor(key, expire = 10) {
this.key = `lock:${key}`;
this.expire = expire;
this.identifier = uuidv4();
}
async acquire() {
const result = await redis.set(
this.key,
this.identifier,
'NX',
'EX',
this.expire
);
return result === 'OK';
}
async release() {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
const result = await redis.eval(script, 1, this.key, this.identifier);
return result === 1;
}
}
// Using the lock
async function withLock(key, callback) {
const lock = new DistributedLock(key);
try {
while (!(await lock.acquire())) {
await new Promise(resolve => setTimeout(resolve, 100));
}
return await callback();
} finally {
await lock.release();
}
}
// Example
await withLock('resource', async () => {
console.log('Executing business logic');
});
Example 3: Rate Limiter
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
async function rateLimit(key, limit = 10, period = 60) {
const current = Math.floor(Date.now() / 1000);
const windowStart = current - period;
const results = await redis
.multi()
.zremrangebyscore(key, 0, windowStart)
.zcard(key)
.zadd(key, current, `${current}`)
.expire(key, period)
.exec();
const count = results[1][1];
return count < limit;
}
// Using the rate limiter
async function apiHandler(userId) {
const allowed = await rateLimit(`api:${userId}`, 10, 60);
if (allowed) {
console.log('Request allowed');
return { success: true };
} else {
console.log('Request denied');
return { success: false, error: 'Rate limit exceeded' };
}
}
Example 4: Message Queue
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
class MessageQueue {
constructor(name) {
this.name = `queue:${name}`;
}
async push(message) {
await redis.rpush(this.name, JSON.stringify(message));
}
async pop(timeout = 0) {
const result = await redis.blpop(this.name, timeout);
if (result) {
return JSON.parse(result[1]);
}
return null;
}
async size() {
return await redis.llen(this.name);
}
}
// Producer
const queue = new MessageQueue('tasks');
await queue.push({ task: 'send_email', to: 'user@example.com' });
// Consumer
while (true) {
const task = await queue.pop(5);
if (task) {
console.log('Processing task:', task);
} else {
break;
}
}
Error Handling
JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();
redis.on('error', (error) => {
console.error('Redis error:', error);
});
redis.on('connect', () => {
console.log('Redis connected');
});
redis.on('ready', () => {
console.log('Redis ready');
});
redis.on('close', () => {
console.log('Redis connection closed');
});
❓ FAQ
Q How do I choose between ioredis and node-redis?
A ioredis is more feature-rich (cluster, sentinel). node-redis is the official client. ioredis is recommended.
Q Is ioredis thread-safe?
A Yes. ioredis has built-in connection pooling and can be shared across modules.
Q How do I handle disconnections?
A ioredis automatically reconnects. You can listen to error and connect events.
Q How do async operations work?
A All ioredis operations return Promises. Use async/await or .then().
Q How do I implement connection pooling?
A ioredis has built-in connection pooling — no extra configuration needed.
📖 Summary
- ioredis: feature-rich, supports cluster, sentinel, Lua scripts
- node-redis: official client, simple to use
- All operations are async, returning Promises
- Supports pipelining, transactions, pub/sub
- Built-in connection pooling and auto-reconnect
- Practical examples: caching, distributed locks, rate limiting, message queues
📝 Exercises
- Basic operations: Use ioredis for string, hash, and list operations
- Pipeline: Use pipelining to batch-set 1000 keys, compare performance
- Pub/Sub: Implement a simple pub/sub messaging system
- Practical: Implement a simple distributed lock or rate limiter
Congratulations!
You have completed all 26 lessons of the Redis tutorial! Next steps:
- Practice: apply Redis in real projects
- Deepen: learn about Redis cluster, sentinel, Lua scripts
- Optimize: learn Redis performance optimization and best practices



