High-performance WebSocket adapter for NestJS using uWebSockets.js
uWestJS is a drop-in replacement for the default NestJS WebSocket adapter, powered by uWebSockets.js. It provides significantly better performance while maintaining full compatibility with NestJS decorators and patterns you already know.
uWebSockets.js is one of the fastest WebSocket implementations available, offering:
- 10x faster than traditional WebSocket libraries
- Lower memory footprint for handling thousands of concurrent connections
- Native backpressure handling to prevent memory issues under load
- Built-in compression support for reduced bandwidth usage
uWestJS brings this performance to NestJS without requiring you to change your existing gateway code.
- Full compatibility with NestJS WebSocket decorators (
@SubscribeMessage,@MessageBody,@ConnectedSocket) - Support for all NestJS middleware: Guards, Pipes, Filters, and Interceptors
- Room-based broadcasting for efficient message distribution
- Built-in CORS configuration
- Automatic message queuing with backpressure handling
- TypeScript support with full type definitions
- Comprehensive test coverage
npm install uwestjsOr using yarn:
yarn add uwestjsOr using pnpm:
pnpm add uwestjs- Node.js 20, 22, 24, or 25
- NestJS >= 11.0.0
- TypeScript >= 6.0.0
- Supported Node.js versions: 20, 22, 24, 25
- If you experience installation or runtime issues, run
npm cache clean --forcebefore installing
Replace your existing WebSocket adapter with uWestJS in your main.ts:
import { NestFactory } from '@nestjs/core';
import { UwsAdapter } from 'uwestjs';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Use uWestJS adapter
app.useWebSocketAdapter(new UwsAdapter(app));
await app.listen(3000);
}
bootstrap();Your existing NestJS gateways work without modification:
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
ConnectedSocket,
} from '@nestjs/websockets';
@WebSocketGateway()
export class ChatGateway {
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: any,
) {
return { event: 'response', data: `Echo: ${data}` };
}
@SubscribeMessage('join')
handleJoin(
@MessageBody() room: string,
@ConnectedSocket() client: any,
) {
client.join(room);
return { event: 'joined', data: room };
}
@SubscribeMessage('broadcast')
handleBroadcast(
@MessageBody() payload: { room: string; message: string },
@ConnectedSocket() client: any,
) {
client.to(payload.room).emit('message', payload.message);
return { event: 'broadcasted' };
}
}After creating your adapter, register your gateway:
import { NestFactory } from '@nestjs/core';
import { UwsAdapter } from 'uwestjs';
import { AppModule } from './app.module';
import { ChatGateway } from './chat.gateway';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);
// Register your gateway
const chatGateway = app.get(ChatGateway);
adapter.registerGateway(chatGateway);
await app.listen(3000);
}
bootstrap();Configure the adapter with various options:
import { UwsAdapter } from 'uwestjs';
import * as uWS from 'uWebSockets.js';
const adapter = new UwsAdapter(app, {
// WebSocket server port
port: 8099,
// Maximum message size (in bytes)
maxPayloadLength: 16384, // 16KB
// Idle timeout (in seconds)
idleTimeout: 60,
// WebSocket endpoint path
path: '/ws',
// Compression settings
compression: uWS.SHARED_COMPRESSOR,
// CORS configuration
cors: {
origin: 'https://example.com', // Specific origin for security
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
},
});Control cross-origin access to your WebSocket server:
// Specific origin (recommended for production)
cors: {
origin: 'https://example.com',
}
// Allow all origins (use only for development/testing)
cors: { origin: '*' }
// Allow multiple origins
cors: {
origin: ['https://example.com', 'https://app.example.com'],
}
// Dynamic origin validation (recommended for flexible security)
cors: {
origin: (origin) => {
return origin?.endsWith('.example.com') ?? false;
},
credentials: true,
}Security Note: Never use origin: '*' with credentials: true in production. This combination is a security risk as it allows any origin to make authenticated requests. Always specify exact origins or use a validation function.
Enable dependency injection for guards, pipes, and filters:
import { ModuleRef } from '@nestjs/core';
const app = await NestFactory.create(AppModule);
const moduleRef = app.get(ModuleRef);
const adapter = new UwsAdapter(app, {
port: 8099,
moduleRef, // Enable DI support
});This allows your guards, pipes, and filters to inject services:
import { Injectable, CanActivate } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class WsAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(context: any): boolean {
try {
const data = context.switchToWs().getData();
const token = data?.token;
if (!token) return false;
const payload = this.jwtService.verify(token);
const client = context.switchToWs().getClient();
client.data = { user: payload, authenticated: true };
return true;
} catch {
return false;
}
}
}
}Protect your WebSocket handlers with guards:
import { UseGuards } from '@nestjs/common';
import { SubscribeMessage, MessageBody } from '@nestjs/websockets';
@WebSocketGateway()
export class SecureGateway {
@UseGuards(WsAuthGuard)
@SubscribeMessage('secure-action')
handleSecureAction(@MessageBody() data: any) {
return { event: 'success', data };
}
}Transform and validate incoming data:
import { UsePipes, ValidationPipe } from '@nestjs/common';
import { SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { IsString, IsNotEmpty } from 'class-validator';
class MessageDto {
@IsString()
@IsNotEmpty()
content: string;
}
@WebSocketGateway()
export class ChatGateway {
@UsePipes(new ValidationPipe())
@SubscribeMessage('message')
handleMessage(@MessageBody() dto: MessageDto) {
return { event: 'message', data: dto.content };
}
}Handle exceptions gracefully:
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseWsExceptionFilter } from '@nestjs/websockets';
@Catch()
export class WsExceptionFilter extends BaseWsExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const client = host.switchToWs().getClient();
client.emit('error', {
message: exception.message,
});
}
}
@UseFilters(WsExceptionFilter)
@WebSocketGateway()
export class ChatGateway {
// Your handlers
}Organize clients into rooms for targeted broadcasting:
@WebSocketGateway()
export class GameGateway {
@SubscribeMessage('join-game')
handleJoinGame(
@MessageBody() gameId: string,
@ConnectedSocket() client: any,
) {
client.join(`game:${gameId}`);
// Notify others in the room
client.to(`game:${gameId}`).emit('player-joined', {
playerId: client.id,
});
return { event: 'joined', gameId };
}
@SubscribeMessage('game-action')
handleGameAction(
@MessageBody() payload: { gameId: string; action: any },
@ConnectedSocket() client: any,
) {
// Broadcast to all clients in the game room
client.to(`game:${payload.gameId}`).emit('game-update', payload.action);
}
@SubscribeMessage('leave-game')
handleLeaveGame(
@MessageBody() gameId: string,
@ConnectedSocket() client: any,
) {
client.leave(`game:${gameId}`);
client.to(`game:${gameId}`).emit('player-left', {
playerId: client.id,
});
}
}Send messages to multiple clients efficiently:
@WebSocketGateway()
export class NotificationGateway {
@SubscribeMessage('notify-all')
notifyAll(@MessageBody() message: string, @ConnectedSocket() client: any) {
// Broadcast to all connected clients
client.broadcast.emit('notification', message);
}
@SubscribeMessage('notify-room')
notifyRoom(
@MessageBody() payload: { room: string; message: string },
@ConnectedSocket() client: any,
) {
// Broadcast to specific room
client.to(payload.room).emit('notification', payload.message);
}
@SubscribeMessage('notify-rooms')
notifyMultipleRooms(
@MessageBody() payload: { rooms: string[]; message: string },
@ConnectedSocket() client: any,
) {
// Broadcast to multiple rooms
client.to(payload.rooms).emit('notification', payload.message);
}
}Connect to your WebSocket server from the client:
// Using native WebSocket
const ws = new WebSocket('ws://localhost:8099');
ws.onopen = () => {
ws.send(JSON.stringify({
event: 'message',
data: 'Hello server!',
}));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log('Received:', response);
};// Using Socket.IO client (compatible)
import { io } from 'socket.io-client';
const socket = io('ws://localhost:8099');
socket.on('connect', () => {
socket.emit('message', 'Hello server!');
});
socket.on('response', (data) => {
console.log('Received:', data);
});- Use rooms for targeted broadcasting instead of iterating through clients manually
- Enable compression for large messages to reduce bandwidth
- Set appropriate
maxPayloadLengthbased on your message sizes - Use
idleTimeoutto automatically disconnect inactive clients - Leverage backpressure handling - the adapter automatically queues messages when clients are slow
The main adapter class that integrates uWebSockets.js with NestJS.
constructor(app: INestApplicationContext, options?: UwsAdapterOptions)registerGateway(gateway: object): void- Register a WebSocket gatewaycreate(port: number, options?: any): any- Create the WebSocket serverbindClientConnect(server: any, callback: Function): void- Bind connection handlerbindClientDisconnect(client: any, callback: Function): void- Bind disconnection handlerbindMessageHandlers(client: any, handlers: MessageMappingProperties[], transform: (data: any) => Observable<any>): void- Bind message handlersclose(server: any): void- Close the WebSocket server
Methods available on the @ConnectedSocket() parameter:
send(data: string | Buffer): boolean- Send data to the clientemit(event: string, data: any): boolean- Send an event with datajoin(room: string | string[]): void- Join one or more roomsleave(room: string | string[]): void- Leave one or more roomsto(room: string | string[]): BroadcastOperator- Target specific rooms for broadcastingbroadcast: BroadcastOperator- Access broadcast operationsclose(): void- Close the connection
Returned by client.to() and client.broadcast for broadcasting operations:
emit(event: string, data: any): void- Broadcast an event to targeted clientsto(room: string | string[]): BroadcastOperator- Add more rooms to targetexcept(room: string | string[]): BroadcastOperator- Exclude specific rooms
If you're migrating from the default Socket.IO adapter:
- Install uWestJS:
npm install uwestjs - Replace the adapter in
main.ts:
// Before
import { IoAdapter } from '@nestjs/platform-socket.io';
app.useWebSocketAdapter(new IoAdapter(app));
// After
import { UwsAdapter } from 'uwestjs';
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);- Register your gateways:
const gateway = app.get(YourGateway);
adapter.registerGateway(gateway);- Update client connections to use the new port (default: 8099)
Your gateway code remains unchanged - all decorators work the same way.
If clients can't connect:
- Verify the port is not blocked by a firewall
- Check that the WebSocket path matches between client and server
- Ensure CORS is configured correctly for your origin
If messages aren't being received:
- Verify the event name matches between client and server
- Check that the message format is valid JSON
- Ensure the gateway is properly registered with
adapter.registerGateway()
If you experience performance problems:
- Increase
maxPayloadLengthif you're sending large messages - Enable compression for bandwidth-intensive applications
- Use rooms for targeted broadcasting instead of iterating clients
- Monitor backpressure - slow clients are automatically handled
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Run all tests
npm test
# Run unit tests only
npm run test:unit
# Run integration tests only
npm run test:integration
# Run tests with coverage
npm run test:cov
# Run tests in watch mode
npm run test:watchThis project is licensed under the MIT License - see the LICENSE file for details.
- Built on top of uWebSockets.js
- Designed for NestJS
- Inspired by the NestJS community's need for high-performance WebSocket solutions
- GitHub Issues: Report a bug
- GitHub Discussions: Ask questions
Vikram Aditya
