Skip to content

Repository files navigation

WebRTC VoIP Application

A full-stack real-time voice calling application built with WebRTC, featuring peer-to-peer audio communication, user authentication, and real-time presence management.

CIFrontend: VercelBackend: Render

Live demo:webrtc-seven-alpha.vercel.app · API health:webrtc-74h2.onrender.com/health

The backend is on Render's free tier and spins down after ~15 minutes of inactivity — the first request after a nap can take up to 30s to wake it back up.

📸 Screenshots

LoginDashboard
Login screenDashboard
Incoming callActive call
Incoming callActive call

Call screenshots were captured with two automated browser sessions placing a real call end-to-end (real signaling, ICE negotiation, and media tracks) — the green test pattern is Chromium's synthetic fake camera feed used for headless capture, not a rendering bug.

📋 Table of Contents

🎯 Application Overview

This WebRTC VoIP application enables users to:

  • Register and authenticate securely
  • Discover other online users in real-time
  • Initiate and receive voice calls using peer-to-peer WebRTC
  • Accept, reject, or end calls
  • Maintain call history and user presence status

The application follows a client-server architecture with peer-to-peer communication for media streaming:

  • Server handles authentication, user management, signaling, and presence
  • Clients communicate directly via WebRTC for audio/video without media passing through the server

🛠 Technology Stack

Backend

LayerTechnology
RuntimeNode.js v18+
FrameworkExpress.js 5.x
LanguageTypeScript 5.x
DatabaseMongoDB 9.x
Cache/SessionRedis 5.x
Real-timeSocket.IO 4.x
AuthenticationJWT + Argon2
LoggingWinston
ValidationZod

Frontend

LayerTechnology
FrameworkAngular 21
LanguageTypeScript 5.x
StylingTailwind CSS 4.x
ComponentsDaisyUI 5.x
Real-timeSocket.IO Client 4.x
StateRxJS Observables
HTTPAngular HttpClient
RoutingAngular Router

DevOps & Tools

  • Containerization: Docker & Docker Compose
  • Version Control: Git
  • Package Manager: npm
  • Build Tool: Angular CLI & TypeScript Compiler

🏗 System Architecture

High-Level Overview

┌─────────────────────────────────────────────────────────────┐
│ Frontend (Angular) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Login │ │ Dashboard │ │ Call UI │ │
│ │ Component │ │ Component │ │ Component │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ▲ ▲ │
│ │ │ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Angular Services Layer │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ │
│ │ │ Auth │ │ Call │ │ WebRTC Service │ │ │
│ │ │ Service │ │ Service │ │ (Peer Conn.) │ │ │
│ │ └─────────┘ └─────────┘ └─────────────────┘ │ │
│ │ ┌─────────────────────┐ ┌─────────────────┐ │ │
│ │ │ Socket Service │ │ HTTP Service │ │ │
│ │ │ (Real-time) │ │ (API calls) │ │ │
│ │ └─────────────────────┘ └─────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└──────────────┬──────────────────────┬───────────────────────┘
│ │
HTTP/REST WebSocket (Socket.IO)
│ │
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Backend (Node.js/Express) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Authentication & Middleware Layer │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │ │
│ │ │ Auth │ │ Request │ │ Error Handling │ │ │
│ │ │Middleware│ │ ID │ │ & Validation │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ API Routes Layer │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │ │
│ │ │ /auth │ │ /calls │ │ /users │ │ │
│ │ │ Routes │ │ Routes │ │ Routes │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Service Layer │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │ │
│ │ │ User │ │ Call │ │ Signaling Service │ │ │
│ │ │ Service │ │ Service │ │ (WebRTC Signaling) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Data Access & Integration Layer │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │ │
│ │ │MongoDB │ │ Redis │ │ Socket.IO Adapter │ │ │
│ │ │Database │ │ Cache │ │ (Pub/Sub) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Logging & Monitoring │ │
│ │ ├─ Winston Logger with Daily Rotation │ │
│ │ ├─ Request ID Tracking │ │
│ │ └─ Error & Performance Monitoring │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
▲ ▲
│ │
REST API WebSocket (Signaling)
│ │
┌──────┴──────┬───────────────┴──────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌─────────────┐
│MongoDB │ │ Redis │ │Socket.IO │
│Database│ │ Cache │ │ Pub/Sub via │
│ │ │ │ │Redis Adapter│
└────────┘ └────────┘ └─────────────┘

Peer-to-Peer Audio Streaming

┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ User A (Frontend) │ │ User B (Frontend) │
│ ┌────────────────────────────────┐ │ │ ┌────────────────────────────────┐ │
│ │ WebRTC Peer Connection │ │ │ │ WebRTC Peer Connection │ │
│ │ ┌──────────────────────────┐ │ │ │ │ ┌──────────────────────────┐ │ │
│ │ │ LocalStream (Microphone) │ │ │ │ │ │ LocalStream (Microphone) │ │ │
│ │ │ ┌──────────────────────┐ │ │ │ │ │ │ ┌──────────────────────┐ │ │ │
│ │ │ │ Audio Tracks (Raw) │ │ │ │ │ │ │ │ Audio Tracks (Raw) │ │ │ │
│ │ │ └──────────────────────┘ │ │ │ │ │ │ └──────────────────────┘ │ │ │
│ │ └──────────────────────────┘ │ │ │ │ └──────────────────────────┘ │ │
│ │ │ │ │ │ │ │ │ │
│ │ ▼ │ │ │ │ ▼ │ │
│ │ ┌──────────────────────────┐ │ │ │ │ ┌──────────────────────────┐ │ │
│ │ │ Add Tracks to Connection │ │ │ │ │ │ Add Tracks to Connection │ │ │
│ │ └──────────────────────────┘ │ │ │ │ └──────────────────────────┘ │ │
│ │ │ │ │ │ │ │ │ │
│ │ ▼ │ │ │ │ ▼ │ │
│ │ ┌──────────────────────────┐ │ │ │ │ ┌──────────────────────────┐ │ │
│ │ │ Codec & Encryption │ │ │ │ │ │ Codec & Encryption │ │ │
│ │ │ (OPUS, DTLS-SRTP) │ │ │ │ │ │ (OPUS, DTLS-SRTP) │ │ │
│ │ │ Remote Stream Reception │ │ │ │ │ │ Remote Stream Reception │ │ │
│ │ └──────────────────────────┘ │ │ │ │ └──────────────────────────┘ │ │
│ └────────────────────────────────┘ │ │ └────────────────────────────────┘ │
│ │ │ │
│ ▲ │ │ ▲ │
└──────────────┼────────────────────────┘ └──────────────────────┼────────────────┘
│ SRTP Media Stream │
│ (Encrypted Audio Data) │
│ │
└─────────────────────────────────────────────────────────┘
Direct P2P Connection (ICE/STUN/TURN)
Signaling Channel (via Socket.IO + Backend):
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 1. User A sends offer via Socket.IO → Backend → User B │
│ 2. User B sends answer via Socket.IO → Backend → User A │
│ 3. Both exchange ICE candidates via Socket.IO → Backend │
│ 4. Once connection established, media flows directly P2P │
└─────────────────────────────────────────────────────────────────────────────────┘

📁 Project Structure

webrtc/
├── backend/ # Node.js/Express backend
│ ├── src/
│ │ ├── @types/ # Global type definitions
│ │ ├── config/ # Environment & configuration
│ │ ├── constants/ # Application constants
│ │ ├── exceptions/ # Custom exceptions
│ │ ├── libs/ # Core libraries
│ │ │ ├── database/ # MongoDB connection
│ │ │ ├── logger/ # Winston logger
│ │ │ ├── redis/ # Redis client
│ │ │ └── socket/ # Socket.IO server & signaling
│ │ ├── middlewares/ # Express middlewares
│ │ │ ├── auth.middleware.ts
│ │ │ ├── error.handler.middleware.ts
│ │ │ ├── request-id.middleware.ts
│ │ │ └── validation.schema.middleware.ts
│ │ ├── models/ # MongoDB schemas
│ │ │ ├── user.model.ts
│ │ │ └── call.model.ts
│ │ ├── routes/ # API routes & controllers
│ │ │ ├── auth/
│ │ │ ├── call/
│ │ │ └── users/
│ │ ├── types/ # TypeScript definitions
│ │ ├── utils/ # Utility functions
│ │ ├── app.ts # Express app setup
│ │ └── index.ts # Entry point
│ ├── Dockerfile
│ ├── docker-compose.yaml
│ ├── package.json
│ ├── tsconfig.json
│ ├── nodemon.json
│ └── README.md
│
├── frontend/ # Angular frontend
│ ├── src/
│ │ ├── app/
│ │ │ ├── components/ # Angular components
│ │ │ │ ├── login/ # Authentication UI
│ │ │ │ ├── dashboard/ # Main app interface
│ │ │ │ └── call/ # Call interface
│ │ │ ├── services/ # Angular services
│ │ │ │ ├── auth.ts # Authentication service
│ │ │ │ ├── call.ts # Call management
│ │ │ │ ├── socket.ts # Socket.IO client
│ │ │ │ └── webrtc.ts # WebRTC peer connection
│ │ │ ├── app.ts # Root component
│ │ │ ├── app.config.ts # App configuration
│ │ │ ├── app.routes.ts # Route definitions
│ │ │ └── styles.css # Global styles
│ │ ├── environments/ # Environment configs
│ │ ├── index.html
│ │ ├── main.ts
│ │ └── styles.css
│ ├── public/
│ ├── angular.json
│ ├── tailwind.config.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
│
├── SETUP.md # Setup and installation guide
└── README.md # This file

✨ Key Features

1. User Authentication & Authorization

  • Registration: Email, username, password
  • Login: JWT-based token authentication, stored in an httpOnly cookie
  • Session Management: Secure cookie handling
  • Password Security: Argon2 hashing

2. Real-Time Presence & User Discovery

  • Online/offline status tracking
  • Last seen timestamp
  • Real-time user list updates via Socket.IO
  • User profiles with avatars

3. WebRTC Voice Calling

  • Signaling: Socket.IO for offer/answer exchange
  • Media: Direct peer-to-peer audio streaming
  • Codecs: OPUS audio codec for high-quality compression
  • Security: DTLS-SRTP for encrypted media
  • NAT Traversal: ICE, STUN, and TURN support
  • Call Lifecycle: Initiate, accept, reject, end

4. Call Management

  • Call history tracking
  • Call status monitoring
  • Call duration tracking
  • In-call notifications
  • Missed call notifications

5. Database & Caching

  • Users: User profiles and authentication
  • Calls: Call history and metadata
  • Sessions: Redis for session management
  • Socket.IO Adapter: Redis pub/sub for horizontal scaling

6. Security Features

  • CORS protection
  • Helmet security headers
  • HPP (HTTP Parameter Pollution) protection
  • Input validation with Zod
  • Request size limiting
  • JWT expiry and refresh

7. Monitoring & Logging

  • Structured logging with Winston
  • Daily log rotation
  • Request ID tracking for tracing
  • Error logging and analytics

🚀 Quick Start

Prerequisites

  • Node.js v18+
  • npm v11+
  • MongoDB 6.0+
  • Redis 7.0+

1. Clone & Setup

git clone <repository-url>cd webrtc
# Backend setupcd backend
cp .env.example .env # Configure environment variables
npm install
npm run dev
# Frontend setup (new terminal)cd frontend
npm install
npm start

2. Access Application

3. Create Test Users

📚 Documentation

Main Docs

Key Topics by Documentation

TopicLocation
Environment SetupSETUP.md → Environment Setup
Backend InstallationSETUP.md → Backend Setup, backend/README.md
Frontend InstallationSETUP.md → Frontend Setup, frontend/README.md
API Endpointsbackend/README.md → API Endpoints
WebSocket Eventsbackend/README.md → WebSocket Events
Database Modelsbackend/README.md → Database Models
Components & Servicesfrontend/README.md → Application Architecture
Docker DeploymentSETUP.md → Docker Setup
TroubleshootingSETUP.md → Troubleshooting

🏛 Architecture Diagrams

Request Flow - User Authentication

1. Frontend Login Form
↓
2. POST /api/auth/login
├─ Email & Password
├─ Backend validates credentials
└─ Sets JWT as an httpOnly cookie (x-app-authorization), valid for 1 day
↓
3. Subsequent API/Socket.IO requests
├─ Browser sends the cookie automatically (withCredentials: true)
└─ Backend verifies the JWT and loads req.user from MongoDB
↓
4. On Cookie Expiry / Invalid Token
├─ Requests receive 401 Unauthorized
└─ Frontend redirects to /login (see authGuard)

Request Flow - Call Initiation

1. User A clicks "Call User B"
↓
2. Frontend: GET /api/users/B (fetch user details)
↓
3. Frontend: Initialize WebRTC Peer Connection
├─ Request microphone permissions
└─ Get local audio stream
↓
4. Frontend: Create SDP Offer
├─ Offer encoded with local ICE candidates
└─ Offer sent to Backend via Socket.IO: "offer"
↓
5. Backend receives offer
├─ Validate sender & recipient
└─ Forward offer to User B via Socket.IO: "offer"
↓
6. User B Frontend receives offer
├─ Initialize Peer Connection
└─ Create SDP Answer
↓
7. User B sends answer to Backend
└─ Backend forwards to User A
↓
8. Both peers exchange ICE Candidates
├─ Via Socket.IO: "ice-candidate"
└─ Backend acts as signaling relay
↓
9. Connection Established
├─ Both RTCPeerConnection states: "connected"
└─ Media flows directly P2P (SRTP encrypted)
↓
10. Call Active
├─ Audio streams through WebRTC
└─ Call metadata stored in MongoDB via Backend

Data Flow - Presence Updates

Browser A (User Login)
↓
Socket.IO Connect
↓
Backend: User joins Socket.IO room
├─ Update user.socketId in MongoDB
├─ Update user.isActive = true
└─ Update user.lastSeen = now
↓
Backend: Broadcast "user-joined" event
├─ Via Redis pub/sub to all instances
└─ To all connected browsers
↓
Browser B, C, D (Other users)
├─ Receive "user-joined" event
└─ Update online users list UI
↓
Browser A (User Logout/Disconnect)
├─ Socket.IO disconnect
└─ Backend updates user.isActive = false
↓
Backend: Broadcast "user-left" event
└─ To all connected browsers

🔄 Communication Flow

1. HTTP REST API Flow

Browser → Frontend Service → HTTP Request → Backend Route Handler
↓ ↓
├─ Authentication ├─ JWT Validation
├─ Error Handling ├─ Business Logic
└─ Response Display └─ Database Query

2. WebSocket (Socket.IO) Flow

Browser ← Socket.IO Client ← Socket Event ← Backend Socket Server
↓ ↓
├─ Real-time Updates ├─ Broadcast to All
├─ Event Listeners ├─ Broadcast to User
└─ Instant Response └─ Broadcast to Room

3. WebRTC Peer Connection Flow

Browser A RTCPeerConnection ←→ Network Stack ←→ Browser B RTCPeerConnection
├─ Microphone Audio (Direct P2P) ├─ Speaker Audio
├─ Codec (OPUS) ├─ Codec (OPUS)
├─ Encryption (DTLS-SRTP) (Encrypted) ├─ Decryption
└─ NAT Traversal (ICE) (via STUN/TURN) └─ NAT Traversal

👨‍💻 Development Guide

Backend Development

Running in Development:

cd backend
npm run dev # Uses nodemon for auto-reload

Available Scripts:

  • npm run dev - Start with nodemon (development)
  • npm run build - Compile TypeScript
  • npm start - Run compiled code

Key Libraries:

  • Express.js - HTTP server framework
  • Socket.IO - Real-time bidirectional communication
  • Mongoose - MongoDB object modeling
  • IORedis - Redis client
  • Winston - Logging library
  • Zod - TypeScript-first schema validation

Frontend Development

Running in Development:

cd frontend
npm start # Starts ng serve on port 4200

Available Scripts:

  • npm start - Start dev server
  • npm run build - Production build
  • npm run watch - Build in watch mode
  • npm test - Run unit tests

Key Libraries:

  • Angular 21 - Framework
  • RxJS - Reactive programming
  • Tailwind CSS - Utility CSS
  • DaisyUI - Component library
  • Socket.IO Client - Real-time communication

Common Development Tasks

Add a New API Endpoint

  1. Create controller in backend/src/routes/[feature]/controllers/
  2. Add validation schema in backend/src/routes/[feature]/validation/
  3. Create service method in service layer
  4. Add route in backend/src/routes/[feature]/[feature].route.ts
  5. Register route in backend/src/routes/index.ts

Add a New Frontend Component

cd frontend
ng generate component components/[component-name]

Add a New Database Model

  1. Create schema in backend/src/models/[model].model.ts
  2. Add Mongoose schema and interfaces
  3. Create service for business logic
  4. Expose via API routes

Add a New Socket.IO Event

  1. Define event handler in backend/src/libs/socket/signaling.service.ts
  2. Emit event from Socket instance
  3. Add listener in frontend/src/app/services/socket.ts
  4. Handle event in component

🔒 Security Considerations

Backend Security

  • ✅ JWT tokens with expiry
  • ✅ Argon2 password hashing
  • ✅ CORS configured per environment
  • ✅ Helmet security headers
  • ✅ Input validation with Zod
  • ✅ Request ID tracking
  • ✅ Error messages don't leak sensitive info

Frontend Security

  • ✅ HTTPS enforced in production
  • ✅ JWT tokens not stored in localStorage (uses memory)
  • ✅ CORS properly configured
  • ✅ XSS protection via Angular sanitization
  • ✅ No sensitive data logged
  • ✅ Secure WebRTC with DTLS-SRTP

WebRTC Security

  • ✅ DTLS-SRTP encryption for media
  • ✅ ICE to prevent address leakage
  • ✅ TURN server for privacy
  • ✅ No unencrypted STUN

📊 Performance Optimization

Backend

  • Redis caching for sessions
  • MongoDB indexing on frequently queried fields
  • Compression middleware
  • Connection pooling
  • Horizontal scaling via Socket.IO Redis adapter

Frontend

  • Lazy loading of routes
  • Change detection optimization
  • Tree-shaking of unused code
  • Gzip compression
  • CDN ready

WebRTC

  • Adaptive bitrate for audio
  • OPUS codec for efficient compression
  • ICE candidate filtering
  • Connection timeout handling

🚢 Deployment

Docker Deployment

cd backend
docker-compose up

Starts:

  • Backend on :3000
  • MongoDB on :27017
  • Redis on :6379

Production Checklist

  • Set NODE_ENV=production
  • Use strong JWT secrets
  • Enable HTTPS
  • Configure CORS for production domain
  • Set database backups
  • Enable monitoring and logging
  • Configure firewall rules
  • Use environment variables for secrets
  • Set up rate limiting
  • Configure reverse proxy (nginx)

📞 Support & Troubleshooting

Common Issues

WebRTC Connection Fails:

  • Check firewall allows UDP
  • Verify STUN/TURN configuration
  • Check browser permissions
  • Review backend logs

Call Audio Not Working:

  • Check microphone permissions
  • Verify media permissions in browser
  • Check browser console for errors
  • Test with different browser

Backend Won't Start:

  • Check MongoDB is running
  • Check Redis is running
  • Verify .env file is configured
  • Check port 3000 is available

Frontend Won't Connect:

  • Verify backend is running
  • Check API URL in environment config
  • Check CORS configuration
  • Review browser console

See SETUP.md for detailed troubleshooting.

📝 Contributing

  1. Create a feature branch
  2. Make changes following code style guidelines
  3. Test thoroughly
  4. Submit pull request
  5. Request code review

📄 License

ISC

👥 Team

Created as part of the km-learn WebRTC learning project.


Last Updated: January 15, 2026

Useful Resources:

About

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages