GAZAR

Principal Engineer | Mentor

Storing and Handling Sessions in Backend - Interview Question

Storing and Handling Sessions in Backend - Interview Question

Sessions play a vital role in web applications, allowing servers to maintain stateful interactions with clients across multiple HTTP requests. In this technical article, we'll explore how to store and manage sessions in a TypeScript server, covering various strategies and best practices. We'll discuss session storage options, session management techniques, and provide code examples using TypeScript to illustrate implementation details.

Session Storage Options:

  • In-Memory Storage: Store session data in server memory. While simple to implement, in-memory storage is not suitable for distributed environments and may lead to scalability issues. (Not Great for Production)
import express, { Request, Response } from 'express';
// Define a type for session data
interface SessionData {
  [key: string]: any;
}

// Create an in-memory store for sessions
const sessionStore: Map<string, SessionData> = new Map();

const app = express();
app.use((req: Request, res: Response, next) => {
  const sessionId = req.headers['session-id'] as string;
  if (!sessionId || !sessionStore.has(sessionId)) {
    const newSessionId = generateSessionId();
    sessionStore.set(newSessionId, {});
    req.headers['session-id'] = newSessionId; // Optionally set session ID in response headers or cookies
  }

  next();
});
  • Database Storage: Persist session data in a database such as PostgreSQL or MongoDB. Database storage offers durability and scalability but may introduce latency due to database access.

If using Prisma:

model Session {
  id       String  @id @default(auto()) @map(name: "session_id")
  userId   String? @map(name: "user_id")
  createdAt DateTime @default(now())
}

And

import express, { Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();
const app = express();
// Middleware to handle session creation and retrieval
app.use(async (req: Request, res: Response, next) => {
  try {
    // Extract session ID from request cookies
    const sessionId = req.cookies.sessionId;

    if (!sessionId) {
      // Create a new session
      const newSession = await prisma.session.create();
      // Set session ID in response cookies
      res.cookie('sessionId', newSession.id, { maxAge: 3600000, httpOnly: true });
    }
    // Pass session data to the next middleware
    next();
  } catch (error) {
    console.error('Error handling session:', error);
    res.status(500).send('Internal Server Error');
  }
});

Or if you use cookies and a package like cookie-session

var cookieSession = require('cookie-session')
var express = require('express')

var app = express()

app.use(cookieSession({
  name: 'session',
  keys: [/* secret keys */],
  // Cookie Options
  maxAge: 24 * 60 * 60 * 1000 // 24 hours
}))
  • External Storage: Utilize external session stores like Redis or Memcached for fast and scalable session management. External stores provide high performance and support features like expiration and clustering.
import express, { Request, Response } from 'express';
import redis from 'redis';

const redisClient = redis.createClient(); // Create a Redis client
const app = express();

app.use((req: Request, res: Response, next) => {
  const sessionId = req.cookies.sessionId;

  if (!sessionId) {
    const newSessionId = generateSessionId();
    res.cookie('sessionId', newSessionId, { maxAge: 3600000, httpOnly: true }); // Set session ID in response cookies
    redisClient.setex(newSessionId, 3600, JSON.stringify({}));
  }
  next();
});

Implementing session management in a TypeScript server is essential for maintaining stateful interactions with clients in web applications. By choosing appropriate session storage options, implementing session management techniques, and leveraging middleware like express-session, developers can ensure secure and efficient session handling in their TypeScript applications. With proper session management in place, applications can provide personalized experiences and maintain user state across multiple HTTP requests effectively.