JavaScript is required to view this website properly.

Building a Scalable Chat Application with NestJS, MongoDB, Redis, WebSockets, Socket.io, and Docker

2026-02-04 · 4 min read

Real-time messaging has become a core feature in modern products. In this post, I’ll walk through how I built a scalable real‑time chat application using NestJS, MongoDB, Redis, WebSockets, Socket.io, and Docker. By the end, you’ll understand the architecture, key implementation details, and how each piece helps the system scale.


1. Introduction

Real‑time messaging powers products across social, collaboration, and enterprise workflows. The goal here is to build a chat app that is reliable, fast, and easy to scale.


2. Project Overview

Features

  • One‑to‑One Messaging for private conversations
  • Group Chats with room‑based messaging
  • Real‑Time Communication via WebSockets and Socket.io
  • Persistent Storage in MongoDB
  • Caching with Redis for better performance
  • Containerization using Docker for repeatable environments

Tech Stack

  • NestJS for a structured, scalable backend
  • MongoDB for chat rooms, messages, and user data
  • Redis for caching and fast access to active state
  • Socket.io for real‑time messaging
  • Docker for containerized services

3. Setting Up the Project

3.1 Initial NestJS Setup

bash
npx @nestjs/cli new chat-app

After setup, create a ChatModule with core files:

text
src/
├── chat/
│   ├── chat.module.ts
│   ├── chat.gateway.ts
│   ├── chat.service.ts
│   └── chat.controller.ts
└── app.module.ts

3.2 MongoDB Setup

Install MongoDB integration:

bash
npm install mongoose @nestjs/mongoose

Example ChatRoom schema:

ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { ParticipantStatus, RoomType } from 'src/shared/schemas/chat.schema';

@Schema({ timestamps: true })
export class ChatRoom extends Document {
  @Prop()
  name: string;

  @Prop({ required: true, enum: RoomType })
  type: RoomType;

  @Prop({ required: true, default: true })
  status: boolean;

  @Prop({ type: String })
  image: string;

  @Prop({
    type: [
      {
        userId: String,
        status: {
          type: String,
          enum: ParticipantStatus,
          default: ParticipantStatus.REQUEST,
        },
        is_deleted: { type: Boolean, default: false },
      },
    ],
    default: [],
  })
  participants: Array<{
    userId: string;
    status: ParticipantStatus;
    is_deleted: boolean;
  }>;

  @Prop({ type: [String], default: [] })
  admins: string[];

  @Prop({ required: true })
  created_by: string;

  @Prop({ required: true })
  updated_by: string;

  @Prop({ default: false })
  is_deleted: boolean;
}

export const ChatRoomSchema = SchemaFactory.createForClass(ChatRoom);

ChatRoomSchema.pre('validate', function (next) {
  if (this.type === RoomType.GROUP && !this.name) {
    next(new Error('Name must be provided for GROUP room type'));
  } else if (this.type === RoomType.SINGLE && this.name) {
    next(new Error('Name should not be provided for SINGLE room type'));
  } else {
    next();
  }
});

ChatRoomSchema.pre('save', function (next) {
  if (this.type === RoomType.SINGLE) {
    this.image = undefined;
  } else if (this.type === RoomType.GROUP && !this.image) {
    this.image = '';
  }
  next();
});

4. WebSocket and Socket.io

NestJS makes real‑time messaging clean with gateways:

ts
import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({ cors: true })
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server;

  handleConnection(client: Socket) {
    console.log('User connected:', client.id);
  }

  handleDisconnect(client: Socket) {
    console.log('User disconnected:', client.id);
  }

  @SubscribeMessage('message')
  handleMessage(client: Socket, payload: any) {
    this.server.to(payload.room).emit('message', payload);
  }
}

5. Redis for Caching

Redis helps reduce database load and keeps active state in memory.

Install:

bash
docker pull redis
npm install redis @nestjs/redis

Example configuration:

ts
// chat.module.ts
providers: [
  ChatService,
  ChatGateway,
  {
    provide: 'REDIS_CLIENT',
    useFactory: () => {
      return new Redis({
        host: process.env.REDIS_HOST,
        port: +process.env.REDIS_PORT,
      });
    },
  },
],

// chat.gateway.ts
@WebSocketGateway(3002, { cors: true })
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
  constructor(
    private readonly chatService: ChatService,
    private readonly chatAuthGuard: ChatAuthGuard,
    @Inject('REDIS_CLIENT') private readonly redisClient: Redis,
  ) {
    this.redisClient.on('connect', () => {
      this.logger.log('Redis connected successfully');
    });

    this.redisClient.on('error', (error) => {
      this.logger.error(`Redis connection error: ${error.message}`);
    });
  }
}

6. Docker Setup

yaml
version: '3'
services:
  app:
    build: .
    ports:
      - '3000:3000'
    depends_on:
      - redis
  redis:
    image: 'redis:latest'
    ports:
      - '6379:6379'

The app connects to Redis using the service name redis.


7. Challenges Faced

  • WebSocket reconnections and resyncing room state
  • Redis connection issues in Docker resolved by using the service name
  • Performance tuning by caching hot data in Redis

8. Lessons Learned

Socket.io + NestJS simplifies real‑time features. Redis is essential for scale, and Docker keeps environments consistent from dev to production.


9. Conclusion

This architecture makes real‑time chat reliable, fast, and scalable. If I continue the project, I’d add authentication, media sharing, and Redis clustering for high availability.

FAQ

Common questions about this kind of project

Short answers for teams evaluating a similar build, migration, or optimization project.

Can you build a project like "Building a Scalable Chat Application with NestJS, MongoDB, Redis, WebSockets, Socket.io, and Docker"?+
Yes. I work on similar full-stack implementations and can scope architecture, delivery timeline, and rollout based on your product goals.
Do you provide remote development support for Engineering?+
Yes. I collaborate remotely with async updates, milestone-based delivery, and timezone-friendly communication.
Can you improve performance and SEO for this kind of project?+
Yes. I optimize Core Web Vitals, technical SEO, structured data, and information architecture for better visibility and conversion.
How do we start?+
Share your current stack and goals on the contact page. I will propose an audit plan with scope, timeline, and implementation path.

Want to build something similar?

I’m available for freelance and full‑time roles. Let’s talk.