The UwsAdapter integrates uWebSockets.js with NestJS for high-performance WebSocket communication.
- Overview
- Constructor
- Configuration Options
- Methods
- Manual Gateway Registration
- HTTP + WebSocket Integration
- Examples
The UwsAdapter provides a high-performance WebSocket implementation using uWebSockets.js. It supports:
- Manual gateway registration for better control
- Multiple gateway support
- Dependency injection for middleware
- Room-based broadcasting
- SSL/TLS support
- HTTP + WebSocket integration (v2.0.0+)
constructor(app: INestApplicationContext, options?: UwsAdapterOptions)Creates a new UwsAdapter instance.
Parameters:
app- NestJS application contextoptions- Optional configuration options
Example:
import { NestFactory } from '@nestjs/core';
import { UwsAdapter } from 'uwestjs';
const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, {
port: 8099,
maxPayloadLength: 16384,
idleTimeout: 60,
});
app.useWebSocketAdapter(adapter);interface UwsAdapterOptions {
port?: number;
maxPayloadLength?: number;
idleTimeout?: number;
compression?: uWS.CompressOptions;
path?: string;
maxBackpressure?: number;
closeOnBackpressureLimit?: boolean;
sendPingsAutomatically?: boolean;
maxLifetime?: number;
cors?: CorsOptions;
moduleRef?: ModuleRef;
uwsApp?: uWS.TemplatedApp;
cert_file_name?: string;
key_file_name?: string;
passphrase?: string;
dh_params_file_name?: string;
ssl_prefer_low_memory_usage?: boolean;
}port?: numberWebSocket server port.
Default: 8099
Example:
new UwsAdapter(app, { port: 3001 });maxPayloadLength?: numberMaximum payload length in bytes. Messages larger than this will be rejected.
Default: 16384 (16KB)
Example:
// Allow 1MB messages
new UwsAdapter(app, { maxPayloadLength: 1024 * 1024 });
// For large file transfers
new UwsAdapter(app, { maxPayloadLength: 10 * 1024 * 1024 }); // 10MBidleTimeout?: numberIdle timeout in seconds. Connections that don't send any data within this time will be automatically closed.
Default: 60 seconds
Example:
// 5 minute timeout
new UwsAdapter(app, { idleTimeout: 300 });
// Disable timeout (not recommended for production)
new UwsAdapter(app, { idleTimeout: 0 });compression?: uWS.CompressOptionsCompression mode for WebSocket messages.
Default: SHARED_COMPRESSOR
Options:
DISABLED- No compressionSHARED_COMPRESSOR- Shared compressor (recommended)DEDICATED_COMPRESSOR_3KBtoDEDICATED_COMPRESSOR_256KB- Various dedicated compressor sizes
Example:
import { UwsAdapter, DISABLED, DEDICATED_COMPRESSOR_3KB } from 'uwestjs';
// Disable compression
new UwsAdapter(app, { compression: DISABLED });
// Use dedicated compressor (higher memory, better compression)
new UwsAdapter(app, { compression: DEDICATED_COMPRESSOR_3KB });path?: stringWebSocket endpoint path.
Default: '/*'
Example:
// Specific path
new UwsAdapter(app, { path: '/ws' });
// Multiple paths (use wildcard)
new UwsAdapter(app, { path: '/api/ws/*' });maxBackpressure?: numberMaximum backpressure (buffered bytes) per WebSocket connection. When a client is slow to receive data, messages are buffered. If the buffer exceeds this limit, behavior depends on closeOnBackpressureLimit.
Default: 1048576 (1MB)
Example:
// Allow 5MB of buffered data per connection
new UwsAdapter(app, { maxBackpressure: 5 * 1024 * 1024 });
// Strict limit for memory-constrained environments
new UwsAdapter(app, { maxBackpressure: 512 * 1024 }); // 512KBcloseOnBackpressureLimit?: booleanClose connection when backpressure limit is exceeded. When true, connections that exceed maxBackpressure are automatically closed. When false, messages continue to buffer (may cause memory issues with slow clients).
Default: false
Example:
// Protect server from slow clients
new UwsAdapter(app, {
maxBackpressure: 1024 * 1024,
closeOnBackpressureLimit: true,
});
// Allow unlimited buffering (use with caution)
new UwsAdapter(app, { closeOnBackpressureLimit: false });sendPingsAutomatically?: booleanAutomatically send ping frames to keep connections alive. When enabled, the server automatically sends WebSocket ping frames to detect dead connections. Clients must respond with pong frames or the connection will be closed after idleTimeout seconds.
Default: true
Example:
// Enable automatic pings (recommended)
new UwsAdapter(app, {
sendPingsAutomatically: true,
idleTimeout: 120, // Close if no pong received within 120s
});
// Disable if client handles pings manually
new UwsAdapter(app, { sendPingsAutomatically: false });maxLifetime?: numberMaximum connection lifetime in minutes. Maximum number of minutes a WebSocket connection may remain open before being automatically closed by the server. Set to 0 to disable this feature. This is useful for forcing clients to reconnect periodically (load balancing), preventing indefinitely long connections, and ensuring clients get updated connection parameters.
Default: 0 (disabled)
Example:
// Close connections after 24 hours
new UwsAdapter(app, { maxLifetime: 24 * 60 }); // 1440 minutes
// Close connections after 1 hour
new UwsAdapter(app, { maxLifetime: 60 });
// Disable (allow indefinite connections)
new UwsAdapter(app, { maxLifetime: 0 });cors?: CorsOptionsCORS configuration for WebSocket connections.
Example:
new UwsAdapter(app, {
cors: {
origin: 'https://example.com',
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
},
});See CORS Options for detailed configuration.
moduleRef?: ModuleRef | NestModuleRefModule reference for dependency injection support. When provided, enables DI for guards, pipes, and filters.
Accepts either the NestJS ModuleRef (auto-wrapped) or our ModuleRef interface.
Important: Without moduleRef, guards/pipes/filters are instantiated directly and cannot have constructor dependencies.
Example:
import { ModuleRef } from '@nestjs/core';
const app = await NestFactory.create(AppModule);
const moduleRef = app.get(ModuleRef);
const adapter = new UwsAdapter(app, {
port: 8099,
moduleRef, // Auto-wrapped internally
});
// Now guards can inject services
@Injectable()
class WsAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {} // DI works!
canActivate(context: any): boolean {
// For WebSocket context: args[0] = socket, args[1] = data payload
const token = context.args[1]?.token;
if (!token) return false;
try {
this.jwtService.verify(token);
return true;
} catch (error) {
return false;
}
}
}cert_file_name?: stringPath to SSL certificate file. Required for HTTPS/WSS.
key_file_name?: stringPath to SSL private key file. Required for HTTPS/WSS.
passphrase?: stringOptional passphrase for encrypted private key.
dh_params_file_name?: stringOptional path to Diffie-Hellman parameters file for enhanced security.
ssl_prefer_low_memory_usage?: booleanOptimize SSL for lower memory usage at the cost of some performance.
Example:
new UwsAdapter(app, {
port: 8099,
cert_file_name: './certs/server.crt',
key_file_name: './certs/server.key',
passphrase: 'your-passphrase',
});uwsApp?: uWS.TemplatedAppProvide an existing uWebSockets.js app instance for HTTP + WebSocket integration (v2.0.0+).
Example:
import { App } from 'uwestjs';
const uwsApp = App();
// HTTP adapter
const httpAdapter = new UwsPlatformAdapter(uwsApp);
const app = await NestFactory.create(AppModule, httpAdapter);
// WebSocket adapter (shares the same uWS instance)
const wsAdapter = new UwsAdapter(app, { uwsApp });
app.useWebSocketAdapter(wsAdapter);interface CorsOptions {
origin?: string | string[] | ((origin: string | null) => boolean);
credentials?: boolean;
methods?: string | string[];
allowedHeaders?: string | string[];
exposedHeaders?: string | string[];
maxAge?: number;
}origin?: string | string[] | ((origin: string | null) => boolean)Allowed origins. Can be a string, array of strings, or a function that returns boolean.
Default: undefined (no CORS)
Examples:
// 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 validation (recommended for flexible security)
cors: {
origin: (origin) => {
// Allow all subdomains of example.com
return origin?.endsWith('.example.com') ?? false;
}
}Security Warning: Never use origin: '*' with credentials: true in production.
credentials?: booleanAllow credentials (cookies, authorization headers, TLS client certificates).
Default: false
methods?: string | string[]Allowed HTTP methods for CORS preflight.
Default: ['GET', 'POST']
allowedHeaders?: string | string[]Headers that clients are allowed to send.
Default: ['Content-Type', 'Authorization']
exposedHeaders?: string | string[]Headers that are exposed to the client.
Default: []
maxAge?: numberHow long (in seconds) the results of a preflight request can be cached.
Default: 86400 (24 hours)
registerGateway(gateway: object): voidManually register a WebSocket gateway for message handling.
Important: We recommend calling registerGateway() manually over bindMessageHandlers() as this provides more metadata control and explicit lifecycle management.
Parameters:
gateway- The gateway instance to register
Example:
const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);
// Manually register your gateway
const chatGateway = app.get(ChatGateway);
adapter.registerGateway(chatGateway);
await app.listen(3000);Why manual registration?
- Better control over metadata scanning and handler registration timing
- Explicit gateway lifecycle management (afterInit, handleConnection, handleDisconnect)
- Clearer separation between adapter initialization and gateway registration
- Allows for custom handler registration strategies
sendToClient(clientId: string, data: unknown): booleanSend a message to a specific client.
Parameters:
clientId- Client identifierdata- Data to send (will be JSON stringified)
Returns: true if sent successfully, false otherwise
Example:
const success = adapter.sendToClient('client-123', {
event: 'notification',
message: 'Hello!',
});broadcast(data: unknown): voidBroadcast a message to all connected clients.
Parameters:
data- Data to send (will be JSON stringified)
Example:
adapter.broadcast({
event: 'announcement',
message: 'Server maintenance in 5 minutes',
});getClientCount(): numberGet the number of connected clients.
Example:
const count = adapter.getClientCount();
console.log(`${count} clients connected`);getClientIds(): string[]Get all connected client IDs.
Example:
const clientIds = adapter.getClientIds();
clientIds.forEach(id => {
console.log(`Client: ${id}`);
});hasClient(clientId: string): booleanCheck if a client is connected.
Example:
if (adapter.hasClient('client-123')) {
adapter.sendToClient('client-123', { event: 'ping' });
}getSocket(clientId: string): UwsSocket | undefinedGet a wrapped socket by client ID.
Example:
const socket = adapter.getSocket('client-123');
if (socket) {
socket.emit('message', { text: 'Hello!' });
}close(server: any): voidClose the server and all client connections.
Example:
// Graceful shutdown
process.on('SIGTERM', () => {
// Pass null when the adapter manages its own server lifecycle
adapter.close(null);
process.exit(0);
});const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);
// Register gateway
const gateway = app.get(EventsGateway);
adapter.registerGateway(gateway);
await app.listen(3000);const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);
// Register multiple gateways
const chatGateway = app.get(ChatGateway);
const gameGateway = app.get(GameGateway);
const notificationGateway = app.get(NotificationGateway);
adapter.registerGateway(chatGateway);
adapter.registerGateway(gameGateway);
adapter.registerGateway(notificationGateway);
await app.listen(3000);Important: If multiple gateways register handlers for the same event, the last registered handler will be invoked. Use unique event names or namespacing to avoid conflicts:
// Gateway1
@SubscribeMessage('chat:message')
handleChatMessage() { }
// Gateway2
@SubscribeMessage('game:message')
handleGameMessage() { }Starting from v2.0.0, you can share a single uWebSockets.js instance between HTTP and WebSocket:
import { NestFactory } from '@nestjs/core';
import { UwsPlatformAdapter, UwsAdapter, App } from 'uwestjs';
// Create shared uWS instance
const uwsApp = App();
// HTTP adapter
const httpAdapter = new UwsPlatformAdapter(uwsApp);
const app = await NestFactory.create(AppModule, httpAdapter);
// WebSocket adapter (shares the same uWS instance)
const wsAdapter = new UwsAdapter(app, { uwsApp });
app.useWebSocketAdapter(wsAdapter);
// Register gateways
const gateway = app.get(EventsGateway);
wsAdapter.registerGateway(gateway);
// Start server (HTTP adapter manages the listening port)
await app.listen(3000);Benefits:
- Single port for both HTTP and WebSocket
- Better resource utilization
- Simplified deployment
import { NestFactory } from '@nestjs/core';
import { UwsAdapter } from 'uwestjs';
const app = await NestFactory.create(AppModule);
const adapter = new UwsAdapter(app, { port: 8099 });
app.useWebSocketAdapter(adapter);
const gateway = app.get(EventsGateway);
adapter.registerGateway(gateway);
await app.listen(3000);const adapter = new UwsAdapter(app, {
port: 8099,
cert_file_name: './certs/server.crt',
key_file_name: './certs/server.key',
passphrase: 'your-passphrase',
});const adapter = new UwsAdapter(app, {
port: 8099,
cors: {
origin: (origin) => origin?.endsWith('.example.com') ?? false,
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
},
});import { ModuleRef } from '@nestjs/core';
const moduleRef = app.get(ModuleRef);
const adapter = new UwsAdapter(app, {
port: 8099,
moduleRef, // Auto-wrapped internally
});import { NestFactory } from '@nestjs/core';
import { ModuleRef } from '@nestjs/core';
import { UwsAdapter, SHARED_COMPRESSOR } from 'uwestjs';
const app = await NestFactory.create(AppModule);
const moduleRef = app.get(ModuleRef);
const adapter = new UwsAdapter(app, {
// Server configuration
port: 8099,
path: '/ws',
// Performance tuning
maxPayloadLength: 1024 * 1024, // 1MB
idleTimeout: 300, // 5 minutes
compression: SHARED_COMPRESSOR,
// CORS configuration
cors: {
origin: (origin) => origin?.endsWith('.example.com') ?? false,
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 3600,
},
// Enable DI for guards/pipes/filters (auto-wrapped)
moduleRef,
});
app.useWebSocketAdapter(adapter);
// Register gateways
const gateway = app.get(EventsGateway);
adapter.registerGateway(gateway);
await app.listen(3000);