0
# WebSocket Handlers
1
2
WebSocket handlers provide event-based interception and mocking of WebSocket connections with support for connection lifecycle management, message broadcasting, and client management.
3
4
## Capabilities
5
6
### WebSocket Link Creation
7
8
Create WebSocket handlers for specific WebSocket server URLs.
9
10
```typescript { .api }
11
/**
12
* Creates a WebSocket link handler for intercepting connections to a specific URL
13
* @param url - WebSocket server URL to intercept (string, RegExp, or path pattern)
14
* @returns WebSocketLink instance with event handling and broadcast capabilities
15
*/
16
function link(url: string | RegExp): WebSocketLink;
17
18
interface WebSocketLink {
19
/** Set of all WebSocket clients connected to this link */
20
clients: Set<WebSocketClientConnectionProtocol>;
21
22
/** Add event listener for WebSocket connection events */
23
addEventListener<EventType extends keyof WebSocketHandlerEventMap>(
24
event: EventType,
25
listener: WebSocketEventListener<EventType>
26
): WebSocketHandler;
27
28
/** Broadcast data to all connected WebSocket clients */
29
broadcast(data: WebSocketData): void;
30
31
/** Broadcast data to all clients except specified ones */
32
broadcastExcept(
33
clients: WebSocketClientConnectionProtocol | WebSocketClientConnectionProtocol[],
34
data: WebSocketData
35
): void;
36
}
37
38
const ws: {
39
link: typeof link;
40
};
41
```
42
43
**Usage Examples:**
44
45
```typescript
46
import { ws } from "msw";
47
48
// Create WebSocket link for chat application
49
const chatServer = ws.link('wss://chat.example.com');
50
51
// Handle new connections
52
chatServer.addEventListener('connection', ({ client }) => {
53
console.log('Client connected:', client.id);
54
55
// Send welcome message to new client
56
client.send(JSON.stringify({
57
type: 'welcome',
58
message: 'Welcome to the chat!'
59
}));
60
61
// Notify other clients about new user
62
chatServer.broadcastExcept(client, JSON.stringify({
63
type: 'user_joined',
64
userId: client.id
65
}));
66
});
67
68
// Create WebSocket link with RegExp pattern
69
const gameServer = ws.link(/wss:\/\/game\.example\.com\/room\/\d+/);
70
71
gameServer.addEventListener('connection', ({ client }) => {
72
// Extract room ID from URL
73
const roomId = client.url.match(/\/room\/(\d+)/)?.[1];
74
75
client.send(JSON.stringify({
76
type: 'room_joined',
77
roomId
78
}));
79
});
80
81
// Development WebSocket server
82
const devServer = ws.link('ws://localhost:8080');
83
84
devServer.addEventListener('connection', ({ client }) => {
85
// Echo server - send back any received message
86
client.addEventListener('message', (event) => {
87
client.send(`Echo: ${event.data}`);
88
});
89
});
90
```
91
92
### Event Handling
93
94
Handle WebSocket connection lifecycle events.
95
96
```typescript { .api }
97
type WebSocketEventListener<EventType extends keyof WebSocketHandlerEventMap> =
98
(...args: WebSocketHandlerEventMap[EventType]) => void;
99
100
interface WebSocketHandlerEventMap {
101
/** Fired when a client connects to the WebSocket server */
102
connection: [event: { client: WebSocketClientConnectionProtocol; server: WebSocketLink }];
103
/** Fired when a client disconnects from the WebSocket server */
104
disconnect: [event: { client: WebSocketClientConnectionProtocol; server: WebSocketLink }];
105
}
106
107
interface WebSocketClientConnectionProtocol {
108
/** Unique client identifier */
109
id: string;
110
/** WebSocket URL the client connected to */
111
url: string;
112
/** Connection protocol (if specified) */
113
protocol?: string;
114
/** Send data to this specific client */
115
send(data: WebSocketData): void;
116
/** Close the connection to this client */
117
close(code?: number, reason?: string): void;
118
/** Add event listener for client-specific events */
119
addEventListener(event: string, listener: (event: any) => void): void;
120
/** Remove event listener from client */
121
removeEventListener(event: string, listener: (event: any) => void): void;
122
}
123
```
124
125
**Usage Examples:**
126
127
```typescript
128
// Handle connection events
129
const server = ws.link('wss://api.example.com');
130
131
server.addEventListener('connection', ({ client, server }) => {
132
console.log(`Client ${client.id} connected to ${client.url}`);
133
134
// Set up client-specific message handler
135
client.addEventListener('message', (event) => {
136
console.log(`Message from ${client.id}:`, event.data);
137
138
// Parse and handle different message types
139
try {
140
const message = JSON.parse(event.data);
141
142
switch (message.type) {
143
case 'chat':
144
// Broadcast chat message to all clients
145
server.broadcast(JSON.stringify({
146
type: 'chat',
147
from: client.id,
148
message: message.text,
149
timestamp: Date.now()
150
}));
151
break;
152
153
case 'ping':
154
// Respond with pong
155
client.send(JSON.stringify({ type: 'pong' }));
156
break;
157
158
case 'join_room':
159
// Handle room joining logic
160
client.send(JSON.stringify({
161
type: 'room_joined',
162
room: message.room
163
}));
164
break;
165
}
166
} catch (error) {
167
client.send(JSON.stringify({
168
type: 'error',
169
message: 'Invalid message format'
170
}));
171
}
172
});
173
174
// Handle client errors
175
client.addEventListener('error', (event) => {
176
console.error(`Client ${client.id} error:`, event);
177
});
178
179
// Handle client disconnect
180
client.addEventListener('close', (event) => {
181
console.log(`Client ${client.id} disconnected:`, event.code, event.reason);
182
183
// Notify other clients
184
server.broadcastExcept(client, JSON.stringify({
185
type: 'user_left',
186
userId: client.id
187
}));
188
});
189
});
190
191
// Handle disconnect events at server level
192
server.addEventListener('disconnect', ({ client }) => {
193
console.log(`Server: Client ${client.id} disconnected`);
194
195
// Clean up any server-side state associated with this client
196
// removeUserFromRooms(client.id);
197
});
198
```
199
200
### Broadcasting Messages
201
202
Send messages to multiple clients with flexible targeting.
203
204
```typescript { .api }
205
/**
206
* Broadcast data to all connected WebSocket clients
207
* @param data - Data to send (string, ArrayBuffer, or Blob)
208
*/
209
broadcast(data: WebSocketData): void;
210
211
/**
212
* Broadcast data to all clients except specified ones
213
* @param clients - Client or array of clients to exclude from broadcast
214
* @param data - Data to send (string, ArrayBuffer, or Blob)
215
*/
216
broadcastExcept(
217
clients: WebSocketClientConnectionProtocol | WebSocketClientConnectionProtocol[],
218
data: WebSocketData
219
): void;
220
221
type WebSocketData = string | ArrayBuffer | Blob;
222
```
223
224
**Usage Examples:**
225
226
```typescript
227
const chatServer = ws.link('wss://chat.example.com');
228
229
chatServer.addEventListener('connection', ({ client, server }) => {
230
client.addEventListener('message', (event) => {
231
const message = JSON.parse(event.data);
232
233
switch (message.type) {
234
case 'broadcast_message':
235
// Send message to all connected clients
236
server.broadcast(JSON.stringify({
237
type: 'message',
238
from: client.id,
239
text: message.text,
240
timestamp: Date.now()
241
}));
242
break;
243
244
case 'private_message':
245
// Send message to all clients except sender
246
server.broadcastExcept(client, JSON.stringify({
247
type: 'message',
248
from: client.id,
249
text: message.text,
250
timestamp: Date.now()
251
}));
252
break;
253
254
case 'admin_announcement':
255
// Send to all clients except specific ones
256
const excludedClients = Array.from(server.clients).filter(
257
c => c.id.startsWith('guest_')
258
);
259
260
server.broadcastExcept(excludedClients, JSON.stringify({
261
type: 'announcement',
262
text: message.text,
263
priority: 'high'
264
}));
265
break;
266
267
case 'server_stats':
268
// Broadcast server statistics
269
server.broadcast(JSON.stringify({
270
type: 'stats',
271
connectedClients: server.clients.size,
272
uptime: process.uptime(),
273
timestamp: Date.now()
274
}));
275
break;
276
}
277
});
278
});
279
280
// Periodic broadcasts
281
const gameServer = ws.link('wss://game.example.com');
282
283
gameServer.addEventListener('connection', ({ server }) => {
284
// Send game state updates every second
285
setInterval(() => {
286
if (server.clients.size > 0) {
287
server.broadcast(JSON.stringify({
288
type: 'game_state',
289
players: server.clients.size,
290
timestamp: Date.now()
291
}));
292
}
293
}, 1000);
294
});
295
296
// Binary data broadcasting
297
const dataServer = ws.link('wss://data.example.com');
298
299
dataServer.addEventListener('connection', ({ client, server }) => {
300
client.addEventListener('message', (event) => {
301
if (event.data instanceof ArrayBuffer) {
302
// Echo binary data to all other clients
303
server.broadcastExcept(client, event.data);
304
}
305
});
306
});
307
```
308
309
### Client Management
310
311
Access and manage connected WebSocket clients.
312
313
```typescript { .api }
314
interface WebSocketLink {
315
/** Set of all WebSocket clients connected to this link */
316
clients: Set<WebSocketClientConnectionProtocol>;
317
}
318
```
319
320
**Usage Examples:**
321
322
```typescript
323
const server = ws.link('wss://monitoring.example.com');
324
325
server.addEventListener('connection', ({ client, server }) => {
326
console.log(`Total clients: ${server.clients.size}`);
327
328
// Send client list to new client
329
client.send(JSON.stringify({
330
type: 'client_list',
331
clients: Array.from(server.clients).map(c => ({
332
id: c.id,
333
url: c.url,
334
protocol: c.protocol
335
}))
336
}));
337
338
client.addEventListener('message', (event) => {
339
const message = JSON.parse(event.data);
340
341
switch (message.type) {
342
case 'get_stats':
343
client.send(JSON.stringify({
344
type: 'stats',
345
totalClients: server.clients.size,
346
clientIds: Array.from(server.clients).map(c => c.id)
347
}));
348
break;
349
350
case 'kick_client':
351
// Find and disconnect specific client
352
const targetClient = Array.from(server.clients).find(
353
c => c.id === message.clientId
354
);
355
356
if (targetClient) {
357
targetClient.close(1000, 'Kicked by admin');
358
}
359
break;
360
361
case 'message_client':
362
// Send message to specific client
363
const recipient = Array.from(server.clients).find(
364
c => c.id === message.targetId
365
);
366
367
if (recipient) {
368
recipient.send(JSON.stringify({
369
type: 'private_message',
370
from: client.id,
371
text: message.text
372
}));
373
}
374
break;
375
}
376
});
377
});
378
379
// Connection limiting
380
const limitedServer = ws.link('wss://limited.example.com');
381
382
limitedServer.addEventListener('connection', ({ client, server }) => {
383
if (server.clients.size > 10) {
384
client.close(1013, 'Server full');
385
return;
386
}
387
388
client.send(JSON.stringify({
389
type: 'welcome',
390
position: server.clients.size,
391
maxClients: 10
392
}));
393
});
394
```
395
396
### Advanced WebSocket Patterns
397
398
Handle complex WebSocket scenarios and patterns.
399
400
```typescript
401
// Room-based messaging
402
const roomServer = ws.link('wss://rooms.example.com');
403
const rooms = new Map<string, Set<WebSocketClientConnectionProtocol>>();
404
405
roomServer.addEventListener('connection', ({ client, server }) => {
406
let currentRoom: string | null = null;
407
408
client.addEventListener('message', (event) => {
409
const message = JSON.parse(event.data);
410
411
switch (message.type) {
412
case 'join_room':
413
// Leave current room
414
if (currentRoom && rooms.has(currentRoom)) {
415
rooms.get(currentRoom)!.delete(client);
416
}
417
418
// Join new room
419
currentRoom = message.room;
420
if (!rooms.has(currentRoom)) {
421
rooms.set(currentRoom, new Set());
422
}
423
rooms.get(currentRoom)!.add(client);
424
425
// Notify room members
426
rooms.get(currentRoom)!.forEach(roomClient => {
427
if (roomClient !== client) {
428
roomClient.send(JSON.stringify({
429
type: 'user_joined_room',
430
userId: client.id,
431
room: currentRoom
432
}));
433
}
434
});
435
break;
436
437
case 'room_message':
438
// Send message to all clients in current room
439
if (currentRoom && rooms.has(currentRoom)) {
440
rooms.get(currentRoom)!.forEach(roomClient => {
441
roomClient.send(JSON.stringify({
442
type: 'room_message',
443
from: client.id,
444
room: currentRoom,
445
text: message.text
446
}));
447
});
448
}
449
break;
450
}
451
});
452
453
client.addEventListener('close', () => {
454
// Clean up room membership
455
if (currentRoom && rooms.has(currentRoom)) {
456
rooms.get(currentRoom)!.delete(client);
457
if (rooms.get(currentRoom)!.size === 0) {
458
rooms.delete(currentRoom);
459
}
460
}
461
});
462
});
463
464
// Protocol-specific handling
465
const multiProtocolServer = ws.link('wss://protocols.example.com');
466
467
multiProtocolServer.addEventListener('connection', ({ client }) => {
468
switch (client.protocol) {
469
case 'chat':
470
client.send(JSON.stringify({ type: 'chat_ready' }));
471
break;
472
case 'game':
473
client.send(JSON.stringify({ type: 'game_ready' }));
474
break;
475
default:
476
client.send(JSON.stringify({ type: 'generic_ready' }));
477
}
478
});
479
```
480
481
## Types
482
483
```typescript { .api }
484
// Handler types
485
interface WebSocketHandler {
486
url: string | RegExp;
487
eventHandlers: Map<string, Function[]>;
488
}
489
490
// Client types
491
interface WebSocketClientConnectionProtocol {
492
id: string;
493
url: string;
494
protocol?: string;
495
send(data: WebSocketData): void;
496
close(code?: number, reason?: string): void;
497
addEventListener(event: string, listener: (event: any) => void): void;
498
removeEventListener(event: string, listener: (event: any) => void): void;
499
}
500
501
// Data types
502
type WebSocketData = string | ArrayBuffer | Blob;
503
504
// Event types
505
interface WebSocketHandlerEventMap {
506
connection: [event: {
507
client: WebSocketClientConnectionProtocol;
508
server: WebSocketLink
509
}];
510
disconnect: [event: {
511
client: WebSocketClientConnectionProtocol;
512
server: WebSocketLink
513
}];
514
}
515
516
type WebSocketEventListener<EventType extends keyof WebSocketHandlerEventMap> =
517
(...args: WebSocketHandlerEventMap[EventType]) => void;
518
519
// Connection types
520
interface WebSocketHandlerConnection {
521
client: WebSocketClientConnectionProtocol;
522
server: WebSocketLink;
523
}
524
```