๐Ÿš€ Multi-Tenant WebSocket Server

Real-time communication with Socket.IO, Express.js & Multi-tenancy support. See live testing.

๐Ÿ“‹ Project Overview

This is a production-ready Node.js WebSocket server built with Express.js and Socket.IO that provides:

๐Ÿข Multi-Tenancy

Isolated namespaces for different clients/applications with separate authentication.

๐Ÿ” Authentication

Token-based authentication for each tenant namespace.

๐Ÿ“ก Broadcasting

HTTP API endpoint for external applications to broadcast messages.

๐ŸŒ CORS Support

Configurable cross-origin resource sharing for web applications.

โœจ Key Features
  • โšก Real-time Communication: Instant bidirectional communication using WebSockets
  • ๐Ÿ—๏ธ Scalable Architecture: Namespace-based multi-tenancy for isolation
  • ๐Ÿ”’ Secure: Token-based authentication and configurable CORS policies
  • ๐Ÿš€ Production Ready: PM2 process management and Apache reverse proxy support
  • ๐Ÿงช Built-in Testing: Web-based test client for easy development and debugging
โš™๏ธ Installation & Setup

1. Clone or Create Project

mkdir mywebsockets && cd mywebsockets

2. Install Dependencies

npm init -y npm install express socket.io cors

3. Project Structure

mywebsockets/ โ”œโ”€โ”€ index.js # Main server file โ”œโ”€โ”€ package.json # Dependencies โ”œโ”€โ”€ start.sh # Startup script โ”œโ”€โ”€ .env # Environment variables โ””โ”€โ”€ public/ # Static files โ””โ”€โ”€ index.html # Test client interface

4. Environment Configuration

Create a .env file:

NODE_ENV=production PORT=3000 DOMAIN=your-domain.com BROADCAST_API_KEY=your_super_secret_api_key TENANT1_SECRET=supersecretkey1 TENANT2_SECRET=anothersecretkey2 TENANT3_SECRET=yetanothersecretkey3

5. Start the Server

# Development npm start # Production with PM2 pm2 start index.js --name websockets-app
๐Ÿ“ก API Reference

HTTP Endpoints

GET /

Description: Serves the built-in test client interface

Response: HTML test interface for WebSocket connections

POST /broadcast

Description: Broadcast messages to connected clients in a specific tenant namespace

Request Body:
{ "tenantId": "tenant1", "message": "Hello everyone!", "apiKey": "your_api_key" }
Response Examples:
// Success { "message": "Message broadcasted successfully" } // No clients connected { "message": "No clients connected to /tenant1. Message not broadcast." } // Error { "error": "Unauthorized: Invalid API Key" }

WebSocket Connection

Connection URL Format

wss://your-domain.com/TENANT_ID?token=TENANT_TOKEN
Example:
const socket = io('wss://websockets.new-cbc.com/tenant1', { query: { token: 'supersecretkey1' } });

Socket Events

Event Direction Description Data
connect Server โ†’ Client Client successfully connected -
disconnect Server โ†’ Client Client disconnected reason (string)
connect_error Server โ†’ Client Connection failed (auth error) error object
chat message Bidirectional Send/receive messages message (string)
๐ŸŒ Hosting Setup (Apache + Ubuntu)

1. Enable Apache Modules

sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod proxy_wstunnel sudo a2enmod rewrite sudo a2enmod ssl sudo systemctl restart apache2

2. Create Apache Virtual Host - HTTP (Port 80)

File: /etc/apache2/sites-available/websockets.new-cbc.com.conf

<VirtualHost *:80> ServerName websockets.new-cbc.com ServerAlias www.websockets.new-cbc.com # Redirect all HTTP to HTTPS RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] ErrorLog ${APACHE_LOG_DIR}/websockets.new-cbc.com_error.log CustomLog ${APACHE_LOG_DIR}/websockets.new-cbc.com_access.log combined </VirtualHost>

3. Create Apache Virtual Host - HTTPS (Port 443)

File: /etc/apache2/sites-available/websockets.new-cbc.com-le-ssl.conf

<IfModule mod_ssl.c> <VirtualHost *:443> ServerName websockets.new-cbc.com ServerAlias www.websockets.new-cbc.com ProxyRequests Off ProxyPreserveHost On ProxyVia On <Proxy *> Require all granted </Proxy> # Proxy for HTTP/HTTPS requests (Socket.IO polling) ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/ # Proxy for WebSocket connections (WSS) RewriteEngine On RewriteCond %{HTTP:Upgrade} =websocket [NC] RewriteRule /(.*) ws://localhost:3000/$1 [P,L] # SSL configuration (Certbot will have added these) SSLCertificateFile /etc/letsencrypt/live/websockets.new-cbc.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/websockets.new-cbc.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf ErrorLog ${APACHE_LOG_DIR}/websockets.new-cbc.com_ssl_error.log CustomLog ${APACHE_LOG_DIR}/websockets.new-cbc.com_ssl_access.log combined </VirtualHost> </IfModule>

4. Get SSL Certificate

sudo apt install certbot python3-certbot-apache sudo certbot --apache -d websockets.new-cbc.com -d www.websockets.new-cbc.com

5. Enable Sites and Restart Apache

sudo a2ensite websockets.new-cbc.com.conf sudo a2ensite websockets.new-cbc.com-le-ssl.conf sudo apache2ctl configtest sudo systemctl restart apache2

6. Set up PM2 for Process Management

# Install PM2 globally sudo npm install -g pm2 # Navigate to project directory cd /var/www/websockets # Start application pm2 start index.js --name websockets-app # Save PM2 configuration pm2 save # Set PM2 to start on boot pm2 startup
โœ… Setup Complete!
Your WebSocket server is now accessible at https://websockets.new-cbc.com
๐Ÿ’ป Usage Examples

Client-Side JavaScript (Web Browser)

// Include Socket.IO client library // <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script> const tenantId = 'tenant1'; const tenantToken = 'supersecretkey1'; // Connect to WebSocket server const socket = io(`https://websockets.new-cbc.com/${tenantId}`, { query: { token: tenantToken } }); // Connection events socket.on('connect', () => { console.log(`Connected to ${tenantId}`); }); socket.on('disconnect', (reason) => { console.log('Disconnected:', reason); }); socket.on('connect_error', (error) => { console.log('Connection failed:', error.message); }); // Message handling socket.on('chat message', (message) => { console.log('Received:', message); // Update your UI here }); // Send message function sendMessage(text) { socket.emit('chat message', text); }

Broadcasting via HTTP API (Server-Side)

// Node.js/Express example const axios = require('axios'); async function broadcastMessage(tenantId, message) { try { const response = await axios.post('https://websockets.new-cbc.com/broadcast', { tenantId: tenantId, message: message, apiKey: 'your_super_secret_api_key' }); console.log('Broadcast successful:', response.data); } catch (error) { console.error('Broadcast failed:', error.response?.data || error.message); } } // Usage broadcastMessage('tenant1', 'Hello all connected clients!');

PHP/Laravel Example

<?php // Laravel example using Guzzle HTTP client use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; class WebSocketBroadcaster { private $client; private $apiKey; private $serverUrl; public function __construct() { $this->client = new Client(); $this->apiKey = config('websocket.api_key'); $this->serverUrl = config('websocket.server_url'); } public function broadcast($tenantId, $message) { try { $response = $this->client->post("{$this->serverUrl}/broadcast", [ 'json' => [ 'tenantId' => $tenantId, 'message' => $message, 'apiKey' => $this->apiKey ] ]); return json_decode($response->getBody(), true); } catch (RequestException $e) { Log::error('WebSocket broadcast failed: ' . $e->getMessage()); return false; } } } // Usage $broadcaster = new WebSocketBroadcaster(); $broadcaster->broadcast('tenant1', 'New notification from Laravel!');

cURL Example

๐Ÿ”ง Troubleshooting

Common Issues

Connection Refused / Can't Connect

# Check if the service is running pm2 status pm2 logs websockets-app # Check if port 3000 is accessible sudo netstat -tlnp | grep :3000 # Restart the service pm2 restart websockets-app

CORS Errors

Update the allowed origins in your index.js:

const allowedOrigins = [ 'https://your-domain.com', 'https://www.your-domain.com' ];

Authentication Failures

  • Verify the tenant token matches the server configuration
  • Check that the tenant ID exists in TENANT_SECRETS
  • Ensure tokens are passed correctly in the query string

WebSocket Connection Issues

# Check Apache modules are enabled sudo a2enmod proxy_wstunnel sudo systemctl restart apache2 # Check Apache error logs sudo tail -f /var/log/apache2/websockets.new-cbc.com_ssl_error.log

Useful Commands

Command Description
pm2 status Check PM2 process status
pm2 logs websockets-app View application logs
pm2 restart websockets-app Restart the application
sudo systemctl status apache2 Check Apache status
sudo apache2ctl configtest Test Apache configuration
curl -I https://your-domain.com Test HTTP connectivity
โš ๏ธ Security Note: Always use HTTPS in production, keep your API keys secure, and regularly update your dependencies and server.

Multi-Tenant WebSocket Server Documentation

Built with Node.js, Express.js, Socket.IO