Solidis LogoSolidis
← Back to Tutorials
Beginner
15 min
Beginner friendly

Building a Session Store with Redis

Learn how to implement a Redis-based session store for web applications using Solidis. Perfect for authentication and user state management.

What You'll Learn
  • How to store and retrieve session data in Redis
  • Implementing session expiration and TTL
  • Creating a session manager class
  • Integrating with Express.js middleware
Prerequisites
  • Node.js 14+ installed
  • Redis server running locally or remotely
  • Basic understanding of Express.js
  • Familiarity with TypeScript (optional)
1
Project Setup
Install dependencies and initialize the project

Install Dependencies

npm install @vcms-io/solidis express uuid

TypeScript (Optional)

npm install -D @types/express @types/uuid typescript
2
Create Session Manager Class
Build a reusable session manager using Solidis
1import { SolidisFeaturedClient } from '@vcms-io/solidis/featured';
2import { v4 as uuidv4 } from 'uuid';
3
4export interface SessionData {
5  userId: string;
6  username: string;
7  email: string;
8  createdAt: number;
9  [key: string]: any;
10}
11
12export class SessionStore {
13  private client: SolidisFeaturedClient;
14  private prefix: string;
15  private ttl: number; // in seconds
16
17  constructor(options: {
18    host?: string;
19    port?: number;
20    prefix?: string;
21    ttl?: number;
22  } = {}) {
23    this.client = new SolidisFeaturedClient({
24      host: options.host || '127.0.0.1',
25      port: options.port || 6379,
26    });
27    this.prefix = options.prefix || 'session:';
28    this.ttl = options.ttl || 3600; // 1 hour default
29  }
30
31  async connect(): Promise<void> {
32    await this.client.connect();
33  }
34
35  async disconnect(): Promise<void> {
36    await this.client.quit();
37  }
38
39  private getKey(sessionId: string): string {
40    return `${this.prefix}${sessionId}`;
41  }
42
43  async create(data: SessionData): Promise<string> {
44    const sessionId = uuidv4();
45    const key = this.getKey(sessionId);
46
47    const sessionData = {
48      ...data,
49      createdAt: Date.now(),
50    };
51
52    await this.client.set(
53      key,
54      JSON.stringify(sessionData),
55      { EX: this.ttl }
56    );
57
58    return sessionId;
59  }
60
61  async get(sessionId: string): Promise<SessionData | null> {
62    const key = this.getKey(sessionId);
63    const data = await this.client.get(key);
64
65    if (!data) {
66      return null;
67    }
68
69    return JSON.parse(data.toString());
70  }
71
72  async update(sessionId: string, data: Partial<SessionData>): Promise<boolean> {
73    const key = this.getKey(sessionId);
74    const existingData = await this.get(sessionId);
75
76    if (!existingData) {
77      return false;
78    }
79
80    const updatedData = { ...existingData, ...data };
81    await this.client.set(
82      key,
83      JSON.stringify(updatedData),
84      { EX: this.ttl }
85    );
86
87    return true;
88  }
89
90  async destroy(sessionId: string): Promise<boolean> {
91    const key = this.getKey(sessionId);
92    const result = await this.client.del(key);
93    return result > 0;
94  }
95
96  async refresh(sessionId: string): Promise<boolean> {
97    const key = this.getKey(sessionId);
98    const result = await this.client.expire(key, this.ttl);
99    return result === 1;
100  }
101
102  async exists(sessionId: string): Promise<boolean> {
103    const key = this.getKey(sessionId);
104    const result = await this.client.exists(key);
105    return result === 1;
106  }
107}
3
Create Express Middleware
Integrate the session store with Express.js
1import express, { Request, Response, NextFunction } from 'express';
2import { SessionStore, SessionData } from './session-store';
3
4// Extend Express Request type
5declare global {
6  namespace Express {
7    interface Request {
8      session?: SessionData;
9      sessionId?: string;
10    }
11  }
12}
13
14export function createSessionMiddleware(store: SessionStore) {
15  return async (req: Request, res: Response, next: NextFunction) => {
16    // Get session ID from cookie
17    const sessionId = req.cookies?.sessionId;
18
19    if (sessionId) {
20      // Try to load existing session
21      const session = await store.get(sessionId);
22
23      if (session) {
24        req.session = session;
25        req.sessionId = sessionId;
26
27        // Refresh session TTL on each request
28        await store.refresh(sessionId);
29      } else {
30        // Session expired, clear cookie
31        res.clearCookie('sessionId');
32      }
33    }
34
35    // Add helper methods to response
36    res.locals.createSession = async (data: SessionData) => {
37      const sessionId = await store.create(data);
38      req.sessionId = sessionId;
39      req.session = data;
40
41      res.cookie('sessionId', sessionId, {
42        httpOnly: true,
43        secure: process.env.NODE_ENV === 'production',
44        maxAge: 3600000, // 1 hour
45        sameSite: 'strict',
46      });
47
48      return sessionId;
49    };
50
51    res.locals.destroySession = async () => {
52      if (req.sessionId) {
53        await store.destroy(req.sessionId);
54        res.clearCookie('sessionId');
55        delete req.session;
56        delete req.sessionId;
57      }
58    };
59
60    next();
61  };
62}
4
Usage Example
Putting it all together in your Express app
1import express from 'express';
2import cookieParser from 'cookie-parser';
3import { SessionStore } from './session-store';
4import { createSessionMiddleware } from './session-middleware';
5
6const app = express();
7const sessionStore = new SessionStore({
8  host: '127.0.0.1',
9  port: 6379,
10  prefix: 'myapp:session:',
11  ttl: 3600, // 1 hour
12});
13
14// Middleware
15app.use(express.json());
16app.use(cookieParser());
17app.use(createSessionMiddleware(sessionStore));
18
19// Login endpoint
20app.post('/api/login', async (req, res) => {
21  const { username, password } = req.body;
22
23  // Validate credentials (simplified)
24  if (username === 'demo' && password === 'password') {
25    const sessionId = await res.locals.createSession({
26      userId: '123',
27      username: 'demo',
28      email: 'demo@example.com',
29      createdAt: Date.now(),
30    });
31
32    return res.json({
33      success: true,
34      sessionId,
35      message: 'Logged in successfully',
36    });
37  }
38
39  res.status(401).json({ error: 'Invalid credentials' });
40});
41
42// Protected endpoint
43app.get('/api/profile', (req, res) => {
44  if (!req.session) {
45    return res.status(401).json({ error: 'Not authenticated' });
46  }
47
48  res.json({
49    user: {
50      userId: req.session.userId,
51      username: req.session.username,
52      email: req.session.email,
53    },
54  });
55});
56
57// Logout endpoint
58app.post('/api/logout', async (req, res) => {
59  await res.locals.destroySession();
60  res.json({ message: 'Logged out successfully' });
61});
62
63// Start server
64async function start() {
65  await sessionStore.connect();
66  app.listen(3000, () => {
67    console.log('Server running on http://localhost:3000');
68  });
69}
70
71start().catch(console.error);
Testing Your Session Store

Test Login

curl -X POST http://localhost:3000/api/login \ -H "Content-Type: application/json" \ -d '{"username":"demo","password":"password"}' \ -c cookies.txt

Test Protected Endpoint

curl http://localhost:3000/api/profile -b cookies.txt

Test Logout

curl -X POST http://localhost:3000/api/logout -b cookies.txt
Best Practices & Tips
  • Use secure cookies in production
    Always set httpOnly, secure, and sameSite flags
  • Implement session rotation
    Regenerate session IDs after login to prevent session fixation attacks
  • Set appropriate TTL values
    Balance between user experience and security based on your application needs
  • Use connection pooling
    Reuse Redis connections across requests for better performance
  • Monitor session counts
    Use SCAN command to track active sessions