How to cache sessions with Redis in Node.js
Storing sessions in Redis enables distributed session management across multiple Node.js servers while providing fast in-memory access. As the creator of CoreUI with 12 years of Node.js backend experience, I’ve implemented Redis session storage for enterprise applications serving millions of concurrent users.
The most scalable approach uses express-session with connect-redis for automatic session serialization and TTL management.
Install Dependencies
Install required packages:
npm install express-session connect-redis redis
Configure Redis Session Store
Create src/config/session.js:
const session = require('express-session')
const RedisStore = require('connect-redis').default
const redis = require('redis')
const redisClient = redis.createClient({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD
})
redisClient.on('error', (err) => {
console.error('Redis error:', err)
})
redisClient.connect()
const sessionConfig = {
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET || 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24
}
}
module.exports = { sessionConfig, redisClient }
Use in Express
const express = require('express')
const session = require('express-session')
const { sessionConfig } = require('./config/session')
const app = express()
app.use(session(sessionConfig))
app.post('/login', (req, res) => {
const { username, password } = req.body
if (authenticate(username, password)) {
req.session.userId = user.id
req.session.username = user.username
req.session.role = user.role
res.json({ message: 'Logged in successfully' })
} else {
res.status(401).json({ message: 'Invalid credentials' })
}
})
app.get('/profile', (req, res) => {
if (!req.session.userId) {
return res.status(401).json({ message: 'Not authenticated' })
}
res.json({
userId: req.session.userId,
username: req.session.username,
role: req.session.role
})
})
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ message: 'Logout failed' })
}
res.clearCookie('connect.sid')
res.json({ message: 'Logged out successfully' })
})
})
Custom Session Operations
class SessionService {
constructor(redisClient) {
this.client = redisClient
}
async getSession(sessionId) {
try {
const data = await this.client.get(`sess:${sessionId}`)
return data ? JSON.parse(data) : null
} catch (error) {
console.error('Get session error:', error)
return null
}
}
async setSession(sessionId, data, ttl = 86400) {
try {
await this.client.setEx(
`sess:${sessionId}`,
ttl,
JSON.stringify(data)
)
} catch (error) {
console.error('Set session error:', error)
}
}
async deleteSession(sessionId) {
try {
await this.client.del(`sess:${sessionId}`)
} catch (error) {
console.error('Delete session error:', error)
}
}
async extendSession(sessionId, ttl = 86400) {
try {
await this.client.expire(`sess:${sessionId}`, ttl)
} catch (error) {
console.error('Extend session error:', error)
}
}
async getAllUserSessions(userId) {
try {
const keys = await this.client.keys('sess:*')
const sessions = []
for (const key of keys) {
const data = await this.client.get(key)
const session = JSON.parse(data)
if (session.userId === userId) {
sessions.push({
sessionId: key.replace('sess:', ''),
...session
})
}
}
return sessions
} catch (error) {
console.error('Get user sessions error:', error)
return []
}
}
async invalidateAllUserSessions(userId) {
try {
const sessions = await this.getAllUserSessions(userId)
for (const session of sessions) {
await this.deleteSession(session.sessionId)
}
} catch (error) {
console.error('Invalidate sessions error:', error)
}
}
}
module.exports = SessionService
Session Middleware
const isAuthenticated = (req, res, next) => {
if (req.session && req.session.userId) {
return next()
}
res.status(401).json({ message: 'Authentication required' })
}
const hasRole = (role) => {
return (req, res, next) => {
if (req.session && req.session.role === role) {
return next()
}
res.status(403).json({ message: 'Insufficient permissions' })
}
}
app.get('/admin', isAuthenticated, hasRole('admin'), (req, res) => {
res.json({ message: 'Admin area' })
})
Session Activity Tracking
const trackActivity = async (req, res, next) => {
if (req.session && req.session.userId) {
req.session.lastActivity = Date.now()
req.session.requestCount = (req.session.requestCount || 0) + 1
}
next()
}
app.use(trackActivity)
Multi-Device Session Management
app.post('/login', async (req, res) => {
const { username, password, deviceId } = req.body
if (authenticate(username, password)) {
req.session.userId = user.id
req.session.deviceId = deviceId
req.session.loginTime = Date.now()
await sessionService.setSession(
`user:${user.id}:device:${deviceId}`,
req.sessionID,
86400
)
res.json({ message: 'Logged in successfully' })
}
})
app.get('/sessions', isAuthenticated, async (req, res) => {
const sessions = await sessionService.getAllUserSessions(req.session.userId)
res.json({ sessions })
})
app.delete('/sessions/:deviceId', isAuthenticated, async (req, res) => {
const sessionId = await sessionService.getSession(
`user:${req.session.userId}:device:${req.params.deviceId}`
)
if (sessionId) {
await sessionService.deleteSession(sessionId)
}
res.json({ message: 'Session terminated' })
})
Session Monitoring
app.get('/admin/sessions/stats', async (req, res) => {
const keys = await redisClient.keys('sess:*')
const activeSessions = keys.length
const sessionData = []
for (const key of keys.slice(0, 100)) {
const data = await redisClient.get(key)
const session = JSON.parse(data)
sessionData.push({
userId: session.userId,
username: session.username,
lastActivity: session.lastActivity
})
}
res.json({
total: activeSessions,
recentSessions: sessionData
})
})
Best Practice Note
This is the same Redis session architecture we use in CoreUI enterprise applications for distributed session management. Redis provides fast in-memory session access with automatic TTL expiration, enabling horizontal scaling across multiple application servers. Always use secure cookies in production and implement session timeout policies.
Related Articles
For caching strategies, you might also want to learn how to cache responses with Redis in Node.js and how to implement caching in Node.js.



