Building VoIP Backends with NestJS: SIP Signalling and FreeSWITCH ESL
How to build scalable VoIP backend APIs with NestJS: FreeSWITCH ESL integration via WebSocket, SIP event handling with decorators, call routing microservices, WebRTC signalling endpoints, and Kubernetes deployment.
Building VoIP Backends with NestJS: SIP Signalling and FreeSWITCH ESL
How to build scalable VoIP backend APIs with NestJS: FreeSWITCH ESL integration via WebSocket, SIP event handling with decorators, call routing microservices, WebRTC signalling endpoints, and Kubernetes deployment.
Kaushik Parmar
Founder & VoIP Architect, CelloIP Technologies
Why NestJS for VoIP Backends?
NestJS brings Angular-inspired structure to Node.js backend development — dependency injection, decorators, modules, and a strong TypeScript foundation. For VoIP backends, these properties are valuable: VoIP signalling servers need robust error handling, clean separation of SIP/WebSocket/HTTP concerns, and testable business logic. NestJS's module system maps naturally to VoIP microservices: a SipModule, a FreeSwitchModule, a BillingModule, a WebRtcModule. Compared to Express, NestJS scales in codebases with multiple developers and makes the call routing logic auditable.
FreeSWITCH ESL Integration with NestJS
FreeSWITCH's Event Socket Layer (ESL) is the primary integration point for NestJS backends. Use the node-esl package to connect to FreeSWITCH's ESL port (8021), subscribe to events, and issue commands. Wrap this in a NestJS provider with lifecycle hooks (onModuleInit to connect, onModuleDestroy to disconnect) and expose it as an injectable service.
freeswitch.service.ts — ESL connection in NestJS
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { Connection } from 'node-esl';
@Injectable()
export class FreeSwitchService implements OnModuleInit, OnModuleDestroy {
private conn: Connection;
async onModuleInit() {
this.conn = new Connection(
process.env.FS_HOST || '127.0.0.1',
8021,
process.env.FS_PASSWORD || 'ClueCon',
);
await new Promise<void>((resolve) => this.conn.on('esl::ready', resolve));
this.conn.subscribe('CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP DTMF');
this.conn.on('esl::event::CHANNEL_ANSWER::*', this.onCallAnswered.bind(this));
}
async onModuleDestroy() { this.conn.disconnect(); }
async originateCall(from: string, to: string): Promise<string> {
const response = await this.conn.api(
`originate {origination_caller_id_number=${from}}sofia/external/${to}@your-gateway &echo()`
);
return response.getBody();
}
private onCallAnswered(event: any) {
const callUuid = event.getHeader('Unique-ID');
// Emit to your call state management service
}
}WebRTC Signalling Gateway in NestJS
NestJS's WebSocket gateway (@WebSocketGateway decorator) is ideal for WebRTC signalling. Clients connect via WebSocket, exchange SDP offers/answers and ICE candidates, and NestJS routes signals between peers. The @SubscribeMessage decorator maps specific message types to handlers. For production, use Redis adapter (socket.io-redis) to share WebSocket state across multiple NestJS instances.
Frequently Asked Questions
NestJS vs Express for VoIP backends?
NestJS for teams of 3+ developers, complex business logic, or microservice architecture. Express for simple single-purpose services (a SIP proxy adapter or webhook receiver). NestJS's structure pays off when the codebase grows beyond a single developer.
Can NestJS handle real-time VoIP events at scale?
Yes, with event streaming via Redis or Kafka. NestJS's microservices module supports Redis pub/sub, RabbitMQ, and Kafka transports out of the box. For FreeSWITCH events at scale, publish ESL events to a Redis stream and let multiple NestJS workers consume them.
How do I deploy a NestJS VoIP backend on Kubernetes?
Build a Docker image from your NestJS app, deploy as a Deployment with 3+ replicas behind a Service. Use horizontal pod autoscaling on CPU/memory. Connect to FreeSWITCH via the cluster-internal Service DNS. Store call state in Redis, not in-process.
Need help implementing this for your project?
Talk to a VoIP Engineer