0
# NestJS Platform WebSocket
1
2
NestJS Platform WebSocket (`@nestjs/platform-ws`) is a WebSocket adapter implementation for the NestJS framework that provides real-time bidirectional communication between server and client applications. It uses the 'ws' library as the underlying WebSocket implementation and integrates seamlessly with NestJS's dependency injection system, decorators, and reactive programming model using RxJS.
3
4
## Package Information
5
6
- **Package Name**: @nestjs/platform-ws
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @nestjs/platform-ws`
10
11
## Core Imports
12
13
```typescript
14
import { WsAdapter } from "@nestjs/platform-ws";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { WsAdapter } = require("@nestjs/platform-ws");
21
```
22
23
## Basic Usage
24
25
```typescript
26
import { WsAdapter } from "@nestjs/platform-ws";
27
import { NestFactory } from "@nestjs/core";
28
import { AppModule } from "./app.module";
29
30
async function bootstrap() {
31
const app = await NestFactory.create(AppModule);
32
33
// Use the WebSocket adapter
34
app.useWebSocketAdapter(new WsAdapter(app));
35
36
await app.listen(3000);
37
}
38
bootstrap();
39
```
40
41
## Architecture
42
43
The @nestjs/platform-ws package is built around the following key components:
44
45
- **WsAdapter Class**: Main adapter extending AbstractWsAdapter from @nestjs/websockets
46
- **Message Parsing**: Configurable message parsing with custom parser support
47
- **Connection Management**: Handles WebSocket server creation, client connections, and lifecycle events
48
- **Server Registry**: Manages multiple WebSocket servers and HTTP server sharing
49
- **RxJS Integration**: Uses observables for reactive message handling and event streams
50
- **Error Handling**: Comprehensive error handling with logging integration
51
52
## Capabilities
53
54
### WebSocket Adapter
55
56
Core WebSocket adapter class that provides complete WebSocket functionality for NestJS applications.
57
58
```typescript { .api }
59
/**
60
* WebSocket adapter implementation for NestJS using the 'ws' library
61
* @publicApi
62
*/
63
class WsAdapter extends AbstractWsAdapter {
64
constructor(
65
appOrHttpServer?: INestApplicationContext | object,
66
options?: WsAdapterOptions
67
);
68
69
/** Creates a WebSocket server with specified configuration */
70
create(
71
port: number,
72
options?: Record<string, any> & {
73
namespace?: string;
74
server?: any;
75
path?: string;
76
}
77
): any;
78
79
/** Sets up message handling for WebSocket client connections */
80
bindMessageHandlers(
81
client: any,
82
handlers: MessageMappingProperties[],
83
transform: (data: any) => Observable<any>
84
): void;
85
86
/** Handles individual message processing and routing */
87
bindMessageHandler(
88
buffer: any,
89
handlersMap: Map<string, MessageMappingProperties>,
90
transform: (data: any) => Observable<any>
91
): Observable<any>;
92
93
/** Attaches error event handlers to WebSocket server */
94
bindErrorHandler(server: any): any;
95
96
/** Binds disconnect event handler to WebSocket client */
97
bindClientDisconnect(client: any, callback: Function): void;
98
99
/** Gracefully closes WebSocket server and terminates client connections */
100
close(server: any): Promise<void>;
101
102
/** Cleans up all HTTP and WebSocket server registries */
103
dispose(): Promise<void>;
104
105
/** Sets custom message parser for processing incoming messages */
106
setMessageParser(parser: WsMessageParser): void;
107
108
/** Protected method to ensure HTTP server exists for the given port */
109
protected ensureHttpServerExists(
110
port: number,
111
httpServer?: any
112
): any;
113
114
/** Protected method to add WebSocket server to internal registry */
115
protected addWsServerToRegistry<T extends Record<'path', string>>(
116
wsServer: T,
117
port: number,
118
path: string
119
): void;
120
}
121
```
122
123
### Message Parsing
124
125
Custom message parser support for handling different message formats.
126
127
```typescript { .api }
128
/**
129
* Message parser function type for processing WebSocket data
130
*/
131
type WsMessageParser = (data: WsData) => { event: string; data: any } | void;
132
133
/** WebSocket data types supported by the parser */
134
type WsData = string | Buffer | ArrayBuffer | Buffer[];
135
```
136
137
### Configuration Options
138
139
Adapter configuration options for customizing behavior.
140
141
```typescript { .api }
142
interface WsAdapterOptions {
143
/** Custom message parser for handling different message formats */
144
messageParser?: WsMessageParser;
145
}
146
```
147
148
### Connection States
149
150
WebSocket connection ready states used internally by the adapter.
151
152
```typescript { .api }
153
enum READY_STATE {
154
CONNECTING_STATE = 0,
155
OPEN_STATE = 1,
156
CLOSING_STATE = 2,
157
CLOSED_STATE = 3
158
}
159
```
160
161
## Usage Examples
162
163
### Basic WebSocket Setup
164
165
```typescript
166
import { WsAdapter } from "@nestjs/platform-ws";
167
import { NestFactory } from "@nestjs/core";
168
169
const app = await NestFactory.create(AppModule);
170
app.useWebSocketAdapter(new WsAdapter(app));
171
```
172
173
### Custom Message Parser
174
175
```typescript
176
import { WsAdapter } from "@nestjs/platform-ws";
177
178
const customParser = (data) => {
179
try {
180
// Custom parsing logic
181
const parsed = JSON.parse(data.toString());
182
return { event: parsed.type, data: parsed.payload };
183
} catch {
184
return null;
185
}
186
};
187
188
const adapter = new WsAdapter(app, { messageParser: customParser });
189
app.useWebSocketAdapter(adapter);
190
```
191
192
### WebSocket Server with Custom Path
193
194
```typescript
195
// In your WebSocket gateway
196
@WebSocketGateway(3001, { path: '/custom-ws' })
197
export class CustomGateway {
198
@WebSocketServer()
199
server: Server;
200
201
// The adapter automatically handles path-based routing
202
}
203
```
204
205
### Multiple WebSocket Servers
206
207
```typescript
208
// Multiple gateways can share the same HTTP server
209
@WebSocketGateway(3001, { path: '/chat' })
210
export class ChatGateway { }
211
212
@WebSocketGateway(3001, { path: '/notifications' })
213
export class NotificationGateway { }
214
215
// The WsAdapter manages multiple servers with different paths
216
```
217
218
### Error Handling
219
220
```typescript
221
@WebSocketGateway()
222
export class MyGateway implements OnGatewayConnection, OnGatewayDisconnect {
223
224
handleConnection(client: WebSocket) {
225
console.log('Client connected');
226
// Connection handling
227
}
228
229
handleDisconnect(client: WebSocket) {
230
console.log('Client disconnected');
231
// Cleanup logic
232
}
233
234
@SubscribeMessage('error')
235
handleError(client: WebSocket, error: any) {
236
console.error('WebSocket error:', error);
237
// Error handling
238
}
239
}
240
```
241
242
### Advanced Configuration
243
244
```typescript
245
import { WsAdapter } from "@nestjs/platform-ws";
246
import { INestApplication } from "@nestjs/common";
247
248
// Custom adapter with advanced configuration
249
class CustomWsAdapter extends WsAdapter {
250
constructor(app: INestApplication) {
251
// Custom message parser that handles binary data
252
const binaryParser = (data: Buffer | string) => {
253
if (Buffer.isBuffer(data)) {
254
// Handle binary WebSocket frames
255
const eventLength = data.readUInt8(0);
256
const event = data.subarray(1, 1 + eventLength).toString();
257
const payload = data.subarray(1 + eventLength);
258
return { event, data: payload };
259
}
260
// Fallback to JSON parsing for text frames
261
return JSON.parse(data.toString());
262
};
263
264
super(app, { messageParser: binaryParser });
265
}
266
267
// Override error handling for custom logging
268
bindErrorHandler(server: any) {
269
server.on('connection', (ws: any) => {
270
ws.on('error', (err: any) => {
271
// Custom error handling logic
272
this.logger.error(`WebSocket error: ${err.message}`, err.stack);
273
});
274
});
275
server.on('error', (err: any) => {
276
this.logger.error(`Server error: ${err.message}`, err.stack);
277
});
278
return server;
279
}
280
}
281
282
// Use custom adapter
283
const app = await NestFactory.create(AppModule);
284
app.useWebSocketAdapter(new CustomWsAdapter(app));
285
```
286
287
### Performance Optimization
288
289
```typescript
290
// Gateway with connection pooling and rate limiting
291
@WebSocketGateway(3001, {
292
cors: true,
293
transports: ['websocket'],
294
})
295
export class OptimizedGateway {
296
private connectionPool = new Map<string, WebSocket>();
297
private messageCount = new Map<string, number>();
298
299
@OnEvent('server.created')
300
handleServerCreated(server: any) {
301
// Configure server-level options after creation
302
server.options.perMessageDeflate = {
303
threshold: 1024,
304
concurrencyLimit: 10,
305
};
306
}
307
308
handleConnection(client: WebSocket, request: any) {
309
const clientId = this.generateClientId(request);
310
311
// Add to connection pool
312
this.connectionPool.set(clientId, client);
313
this.messageCount.set(clientId, 0);
314
315
// Set up rate limiting
316
const rateLimit = setInterval(() => {
317
this.messageCount.set(clientId, 0);
318
}, 60000); // Reset every minute
319
320
client.on('close', () => {
321
clearInterval(rateLimit);
322
this.connectionPool.delete(clientId);
323
this.messageCount.delete(clientId);
324
});
325
}
326
327
@SubscribeMessage('message')
328
handleMessage(client: WebSocket, data: any) {
329
const clientId = this.getClientId(client);
330
const count = this.messageCount.get(clientId) || 0;
331
332
// Rate limiting
333
if (count > 100) { // Max 100 messages per minute
334
client.send(JSON.stringify({
335
error: 'Rate limit exceeded'
336
}));
337
return;
338
}
339
340
this.messageCount.set(clientId, count + 1);
341
// Process message...
342
}
343
344
private generateClientId(request: any): string {
345
return `${request.socket.remoteAddress}:${Date.now()}`;
346
}
347
348
private getClientId(client: WebSocket): string {
349
// Implementation to retrieve client ID
350
return '';
351
}
352
}
353
```
354
355
## Types
356
357
```typescript { .api }
358
/** HTTP server registry key type - uses port number as key */
359
type HttpServerRegistryKey = number;
360
361
/** HTTP server registry entry type - stores HTTP server instances */
362
type HttpServerRegistryEntry = any;
363
364
/** WebSocket server registry key type - uses port number as key */
365
type WsServerRegistryKey = number;
366
367
/** WebSocket server registry entry type - array of WebSocket servers */
368
type WsServerRegistryEntry = any[];
369
370
/** Port constant used for underlying HTTP server sharing */
371
const UNDERLYING_HTTP_SERVER_PORT = 0;
372
```
373
374
## Internal Registry Management
375
376
The adapter maintains internal registries for HTTP and WebSocket servers:
377
378
```typescript { .api }
379
/** Map storing HTTP servers by port number */
380
protected readonly httpServersRegistry: Map<HttpServerRegistryKey, HttpServerRegistryEntry>;
381
382
/** Map storing WebSocket servers by port number */
383
protected readonly wsServersRegistry: Map<WsServerRegistryKey, WsServerRegistryEntry>;
384
```
385
386
## Error Handling
387
388
The adapter provides comprehensive error handling:
389
390
- **Connection Errors**: Automatically logged and handled through the NestJS logger
391
- **Message Parsing Errors**: Returns empty observable for invalid messages
392
- **Server Errors**: Bound to error event handlers with logging
393
- **Namespace Errors**: Throws explicit error if namespaces are used (not supported)
394
- **Upgrade Errors**: Socket connections are terminated for invalid upgrade requests
395
- **Malformed URLs**: HTTP 400 responses for invalid WebSocket upgrade URLs
396
397
### Error Scenarios
398
399
```typescript
400
// Namespace error (thrown during server creation)
401
const error = new Error(
402
'"WsAdapter" does not support namespaces. If you need namespaces in your project, consider using the "@nestjs/platform-socket.io" package instead.'
403
);
404
405
// Message parsing error (returns EMPTY observable)
406
try {
407
const message = this.messageParser(buffer.data);
408
if (!message) {
409
return EMPTY; // Invalid message format
410
}
411
} catch {
412
return EMPTY; // Parsing failed
413
}
414
415
// HTTP upgrade error handling
416
httpServer.on('upgrade', (request, socket, head) => {
417
try {
418
// URL parsing and routing logic
419
} catch (err) {
420
socket.end('HTTP/1.1 400\r\n' + err.message);
421
}
422
});
423
```