# Video Call Backend Server
A Node.js backend server for the video calling application, built with Express and Socket.io. This server handles user authentication, real-time signaling for WebRTC connections, and manages user presence.
## π Features
- **Real-time Signaling** - WebRTC signaling server using Socket.io
- **User Management** - In-memory user storage and presence tracking
- **Call Management** - Handle call initiation, acceptance, rejection, and termination
- **Authentication API** - Simple REST API for user login
- **ICE Candidate Exchange** - Facilitate peer-to-peer connection establishment
- **Connection Health** - Ping/pong for connection monitoring
## π Prerequisites
- **Node.js** (v16 or higher)
- **npm** or **yarn**
## π οΈ Installation
### 1. Navigate to Server Directory
cd server
### 2. Install Dependencies
npm install
### 3. Start the Server
\# Production
npm start
\# Development (with auto-restart)
npm run dev
The server will start on http://localhost:3001
## π¦ Dependencies
### Production Dependencies
{
"express": "^4.18.2", // Web framework
"socket.io": "^4.7.2", // WebSocket server
"cors": "^2.8.5", // Cross-origin requests
"uuid": "^9.0.0" // Unique ID generation
}
### Development Dependencies
{
"nodemon": "^3.0.1" // Auto-restart during development
}
## ποΈ Server Architecture
server/
βββ index.js # Main server file
βββ package.json # Dependencies and scripts
βββ package-lock.json # Dependency lock file
βββ README.md # This file
## π API Endpoints
### REST API
#### POST /api/login
Authenticate a user and return user data.
**Request:**
{
"username": "john\_doe",
"password": "any\_password"
}
**Response:**
{
"user": {
"id": "uuid-string",
"username": "john\_doe",
"isOnline": false
},
"token": "uuid-string"
}
**Error Response:**
{
"error": "Username and password required"
}
#### GET /api/users
Get all users (online and offline).
**Response:**
\[
{
"id": "uuid-string",
"username": "john\_doe",
"isOnline": true
}
]
#### GET /api/online-users
Get only online users.
**Response:**
\[
{
"id": "uuid-string",
"username": "john\_doe",
"isOnline": true,
"socketId": "socket-id"
}
]
## π Socket.io Events
### Client β Server Events
#### join
User joins the server and goes online.
socket.emit('join', {
id: 'user-uuid',
username: 'john\_doe'
});#### call\_user
Initiate a call to another user.
socket.emit('call\_user', {
to: 'target-user-id',
from: { id: 'caller-id', username: 'caller' },
callType: 'video', // or 'audio'
offer: rtcSessionDescription
});#### accept\_call
Accept an incoming call.
socket.emit('accept\_call', {
callId: 'call-uuid',
answer: rtcSessionDescription
});#### reject\_call
Reject an incoming call.
socket.emit('reject\_call', {
callId: 'call-uuid'
});#### end\_call
End an active call.
socket.emit('end\_call', {
callId: 'call-uuid'
});#### ice\_candidate
Exchange ICE candidates for WebRTC connection.
socket.emit('ice\_candidate', {
to: 'target-user-id',
candidate: rtcIceCandidate
});#### ping
Health check ping.
socket.emit('ping');### Server β Client Events
#### join\_success
Confirmation of successful join.
{
message: 'Successfully connected to the server',
onlineUsers: \[/\* array of online users \*/]
}#### users\_updated
Broadcast when user list changes.
\[/\* array of currently online users \*/]#### incoming\_call
Notify user of incoming call.
{
callId: 'call-uuid',
from: { id: 'caller-id', username: 'caller' },
callType: 'video',
offer: rtcSessionDescription
}#### call\_accepted
Notify caller that call was accepted.
{
callId: 'call-uuid',
answer: rtcSessionDescription
}#### call\_rejected
Notify caller that call was rejected.
{
callId: 'call-uuid'
}#### call\_ended
Notify that call has ended.
{
callId: 'call-uuid'
}#### call\_status
Update on call status.
{
status: 'ringing', // or 'user\_offline'
callId: 'call-uuid'
}#### ice\_candidate
Forward ICE candidate to peer.
{
from: 'sender-user-id',
candidate: rtcIceCandidate
}#### pong
Response to ping.
// No data, just event## πΎ Data Storage
### In-Memory Storage
The server uses in-memory storage for demo purposes:
const users = new Map(); // All users (persistent)
const activeUsers = new Map(); // Currently online users
const calls = new Map(); // Active calls### User Object Structure
{
id: 'uuid-string',
username: 'john\_doe',
isOnline: true,
socketId: 'socket-id',
lastSeen: '2023-12-01T10:30:00.000Z'
}### Call Object Structure
{
id: 'call-uuid',
from: { id: 'caller-id', username: 'caller' },
to: { id: 'callee-id', username: 'callee' },
callType: 'video',
status: 'ringing',
startTime: 1701423000000
}## π§ Configuration
### Environment Variables
PORT=3001 # Server port (default: 3001)
NODE\_ENV=development # Environment mode
### CORS Configuration
const io = socketIo(server, {
cors: {
origin: "http://localhost:5173", // Frontend URL
methods: \["GET", "POST"]
}
});### Express Middleware
app.use(cors()); // Enable CORS
app.use(express.json()); // Parse JSON bodies## π Logging & Debugging
### Console Logs
The server provides detailed logging:
- π Connection events
- π₯ User join/leave events
- π Call state changes
- π§ ICE candidate exchanges
- β Error conditions
### Debug Mode
Enable detailed logging:
DEBUG=socket.io\* npm start
## π Deployment
### Local Development
npm run dev # Uses nodemon for auto-restart
### Production Deployment
#### 1. **Heroku**
\# Create Procfile
echo "web: node index.js" > Procfile
\# Deploy
git add .
git commit -m "Deploy to Heroku"
git push heroku main
#### 2. **Railway**
\# railway.json
{
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "node index.js"
}
}
#### 3. **DigitalOcean App Platform**
\# .do/app.yaml
name: video-call-server
services:
\- name: api
source\_dir: /server
github:
repo: your-repo
branch: main
run\_command: node index.js
environment\_slug: node-js
instance\_count: 1
instance\_size\_slug: basic-xxs
### Production Considerations
#### 1. **Environment Variables**
PORT=80
NODE\_ENV=production
CORS\_ORIGIN=https://your-frontend-domain.com
#### 2. **Process Management**
\# Using PM2
npm install -g pm2
pm2 start index.js --name "video-call-server"
pm2 startup
pm2 save
#### 3. **Reverse Proxy (Nginx)**
server {
listen 80;
server\_name your-domain.com;
location / {
proxy\_pass http://localhost:3001;
proxy\_http\_version 1.1;
proxy\_set\_header Upgrade $http\_upgrade;
proxy\_set\_header Connection 'upgrade';
proxy\_set\_header Host $host;
proxy\_cache\_bypass $http\_upgrade;
}
}
## π‘οΈ Security Considerations
### Current Limitations (Demo Only)
- **No real authentication** - accepts any username/password
- **In-memory storage** - data lost on restart
- **No rate limiting** - vulnerable to spam
- **No input validation** - accepts any data
- **No encryption** - messages sent in plain text
### Production Security Measures
// Rate limiting
const rateLimit = require('express-rate-limit');
app.use(rateLimit({
windowMs: 15 \* 60 \* 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
}));
// Input validation
const { body, validationResult } = require('express-validator');
app.post('/api/login', \[
body('username').isLength({ min: 3 }).trim().escape(),
body('password').isLength({ min: 6 })
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// ... rest of login logic
});
// Helmet for security headers
const helmet = require('helmet');
app.use(helmet());## π Performance Monitoring
### Health Check Endpoint
app.get('/health', (req, res) => {
res.json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
activeUsers: activeUsers.size,
activeCalls: calls.size
});
});### Metrics to Monitor
- **Active connections** - Number of connected users
- **Memory usage** - Server memory consumption
- **Call success rate** - Percentage of successful calls
- **Response time** - API endpoint response times
## π Troubleshooting
### Common Issues
#### 1. **Port Already in Use**
\# Find process using port 3001
lsof -i :3001
\# Kill the process
kill -9 <PID>
#### 2. **CORS Errors**
- Update CORS origin in server configuration
- Ensure frontend URL matches CORS settings
#### 3. **Socket Connection Failed**
- Check if server is running
- Verify firewall settings
- Ensure WebSocket support is enabled
#### 4. **Memory Leaks**
- Monitor memory usage with process.memoryUsage()
- Implement proper cleanup for disconnected users
- Clear inactive calls periodically
### Debug Commands
\# Check server status
curl http://localhost:3001/health
\# Test login endpoint
curl -X POST http://localhost:3001/api/login \\
-H "Content-Type: application/json" \\
-d '{"username":"test","password":"test"}'
\# Monitor logs
tail -f server.log
## π API Testing
### Using curl
\# Test login
curl -X POST http://localhost:3001/api/login \\
-H "Content-Type: application/json" \\
-d '{"username":"testuser","password":"testpass"}'
\# Get users
curl http://localhost:3001/api/users
\# Get online users
curl http://localhost:3001/api/online-users
### Using Postman
Import the following collection:
{
"info": { "name": "Video Call API" },
"item": \[
{
"name": "Login",
"request": {
"method": "POST",
"url": "http://localhost:3001/api/login",
"body": {
"mode": "raw",
"raw": "{\\"username\\":\\"test\\",\\"password\\":\\"test\\"}"
}
}
}
]
}
## π Additional Resources
- [Socket.io Documentation](https://socket.io/docs/)
- [Express.js Guide](https://expressjs.com/en/guide/)
- [WebRTC Signaling](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC\_API/Signaling\_and\_video\_calling)
- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
**Server ready for video calling! π**