A scalable, containerized real-time chat server built with Node.js, Socket.io, Redis, and MongoDB. Designed to serve multiple applications with full multi-tenancy support.
- Multi-Tenancy: Isolate conversations by project ID
- General Chat Rooms: Public chat channels per project
- Direct Messaging: 1:1 private conversations
- Offline Messaging: Messages are persisted and delivered when users come online
- Horizontal Scaling: Redis adapter enables multiple server instances
- Load Balancing: Nginx with sticky sessions for WebSocket connections
- Message Persistence: MongoDB stores complete chat history
- Full Containerization: Docker Compose orchestrates all services
βββββββββββββββ
β Clients β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββββββ
β Nginx (Port 80) β β Load Balancer
ββββββββββ¬βββββββββ
β
ββββββ΄βββββ
βΌ βΌ
ββββββββββ ββββββββββ
β Server β β Server β β Chat Server Instances
β 1 β β 2 β
βββββ¬βββββ βββββ¬βββββ
β β
ββββββ¬ββββββ
β
ββββββ΄ββββββ
βΌ βΌ
ββββββββββ ββββββββββ
β Redis β β MongoDBβ β Data Layer
ββββββββββ ββββββββββ
- chatty-nginx: Nginx reverse proxy/load balancer
- chatty-server: Node.js + Socket.io (2 replicas)
- chatty-redis: Pub/Sub for cross-server messaging
- chatty-mongo: Persistent message storage
- Docker & Docker Compose
- Node.js 18+ (for local development/testing)
cd c:/desarrollo/gravity
docker-compose up -d --buildThis starts all services:
- Nginx on port
80 - 2 chat server instances
- Redis and MongoDB (internal only)
docker psYou should see 5 containers running with chatty_ prefix.
docker run --rm --network chatty_chat-net \
-e SOCKET_URL=http://chatty-nginx:80 \
-v ${PWD}:/app -w /app node:18-alpine \
sh -c "npm install socket.io-client ts-node typescript --quiet && npx ts-node test-client.ts"cd r_chat
npm install
npm run webOpen your browser and connect with:
- Project ID:
my-app - User ID:
user-1
Connect to the server with query parameters:
import { io } from 'socket.io-client';
const socket = io('http://localhost:80', {
query: {
projectId: 'my-app',
userId: 'user-123'
}
});| Event | Payload | Description |
|---|---|---|
join_general |
- | Join the project's general chat room |
join_dm |
{ targetUserId: string } |
Join a 1:1 DM room with another user |
send_message |
{ room: string, message: string } |
Send a message to a room |
| Event | Payload | Description |
|---|---|---|
connect |
- | Successfully connected to server |
history |
{ room: string, messages: Message[] } |
Chat history when joining a room |
receive_message |
{ projectId, room, userId, message, timestamp } |
New message received |
disconnect |
- | Disconnected from server |
- General Chat:
${projectId}:general - Direct Message:
${projectId}:dm:${user1}:${user2}(sorted alphabetically)
// Join general chat
socket.emit('join_general');
// Send message to general
socket.emit('send_message', {
room: 'my-app:general',
message: 'Hello everyone!'
});
// Join DM with user-456
socket.emit('join_dm', { targetUserId: 'user-456' });
// Send DM
socket.emit('send_message', {
room: 'my-app:dm:user-123:user-456',
message: 'Hey there!'
});
// Listen for messages
socket.on('receive_message', (data) => {
console.log(`${data.userId}: ${data.message}`);
});- Project Isolation: Users can only access rooms within their project
- Room Validation: Server validates room names match the user's project
- Internal Services: Redis and MongoDB are not exposed to the host
- Connection Authentication: projectId and userId required to connect
gravity/
βββ src/
β βββ server.ts # Main server logic
βββ r_chat/ # React Native client
β βββ app/
β β βββ index.tsx # Connection screen
β β βββ chat.tsx # Chat screen
β β βββ _layout.tsx # Navigation
βββ docker-compose.yml # Container orchestration
βββ Dockerfile # Server container
βββ nginx.conf # Load balancer config
βββ test-client.ts # Test script
βββ .env # Environment variables
- Install dependencies:
npm install-
Create
tsconfig.json(already exists) -
Run tests:
npx ts-node test-client.tsCreate .env file:
COMPOSE_PROJECT_NAME=chattydocker logs chatty_chatty-server_1
docker logs chatty_chatty-server_2docker psdocker exec -it chatty_chatty-mongo_1 mongosh
use chat-db
db.messages.find()To add more server instances, update docker-compose.yml:
chat-server:
deploy:
replicas: 5 # Increase from 2Then restart:
docker-compose up -d --scale chatty-server=5The project includes comprehensive tests for:
- β Multi-project isolation
- β General chat functionality
- β Direct messaging
- β Offline message delivery
- β Cross-server communication (via Redis)
Run tests:
npm run test # Run test-client.ts in container-
Use environment variables for configuration:
environment: - MONGO_URI=${MONGO_URI} - REDIS_HOST=${REDIS_HOST}
-
Enable SSL in Nginx:
listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem;
-
Use managed databases (MongoDB Atlas, Redis Cloud)
-
Add authentication middleware for enhanced security
-
Implement rate limiting in Nginx
MongoDB stores messages with the following structure:
{
projectId: string; // Tenant identifier
room: string; // Room name
userId: string; // Sender ID
message: string; // Message content
timestamp: Date; // When sent
}Indexes exist on projectId and room for fast queries.
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
ISC
docker-compose down
docker ps # Check for conflicting containersdocker-compose logs chatty-server
docker-compose logs chatty-mongo- Ensure Docker containers are running
- Check
SOCKET_URLpoints to correct host - Verify firewall settings
- Check Redis connection in logs
- Verify Redis adapter is initialized
For issues or questions, please open an issue on the repository.
Built with β€οΈ using Node.js, Socket.io, Redis, MongoDB, and Docker.