0
# Channel Communication
1
2
Channel-based messaging system for subscribing to topics and exchanging real-time messages with Phoenix applications, including join/leave lifecycle and event handling.
3
4
## Capabilities
5
6
### Channel Constructor
7
8
Creates a new Channel instance for a specific topic.
9
10
```typescript { .api }
11
/**
12
* Creates a new Channel instance for a specific topic
13
* @param topic - Channel topic string (e.g., "room:lobby", "user:123")
14
* @param params - Channel parameters (static object or dynamic function)
15
* @param socket - Socket instance (optional, can be set later)
16
*/
17
constructor(topic: string, params?: object | (() => object), socket?: Socket);
18
```
19
20
**Usage Example:**
21
22
```typescript
23
import { Channel, Socket } from "phoenix";
24
25
const socket = new Socket("/socket");
26
27
// Static parameters
28
const lobbyChannel = new Channel("room:lobby", { user_id: 123 });
29
30
// Dynamic parameters
31
const userChannel = new Channel("user:456", () => ({
32
token: getCurrentUserToken(),
33
timestamp: Date.now()
34
}));
35
36
// With socket
37
const chatChannel = new Channel("chat:general", { role: "member" }, socket);
38
```
39
40
### Channel Properties
41
42
Access channel state and topic information.
43
44
```typescript { .api }
45
/**
46
* Current channel state
47
*/
48
readonly state: ChannelState;
49
50
/**
51
* Channel topic string
52
*/
53
readonly topic: string;
54
55
type ChannelState = "closed" | "errored" | "joined" | "joining" | "leaving";
56
```
57
58
**Usage Example:**
59
60
```typescript
61
console.log("Channel topic:", channel.topic);
62
console.log("Channel state:", channel.state);
63
64
// Check if channel is ready for messages
65
if (channel.state === "joined") {
66
channel.push("new_message", { body: "Hello!" });
67
}
68
```
69
70
### Channel Lifecycle
71
72
Join and leave channels with timeout handling.
73
74
```typescript { .api }
75
/**
76
* Join the channel
77
* @param timeout - Join timeout in milliseconds (optional)
78
* @returns Push instance for handling join response
79
*/
80
join(timeout?: number): Push;
81
82
/**
83
* Leave the channel
84
* @param timeout - Leave timeout in milliseconds (optional)
85
* @returns Push instance for handling leave response
86
*/
87
leave(timeout?: number): Push;
88
```
89
90
**Usage Example:**
91
92
```typescript
93
// Basic join
94
channel.join()
95
.receive("ok", (response) => {
96
console.log("Joined successfully:", response);
97
})
98
.receive("error", (response) => {
99
console.error("Failed to join:", response);
100
})
101
.receive("timeout", () => {
102
console.warn("Join timed out");
103
});
104
105
// Join with custom timeout (5 seconds)
106
channel.join(5000)
107
.receive("ok", ({ messages }) => {
108
console.log("Catching up on messages:", messages);
109
});
110
111
// Leave channel
112
channel.leave()
113
.receive("ok", () => {
114
console.log("Left channel successfully");
115
});
116
```
117
118
### Event Handling
119
120
Register and remove event listeners for channel messages.
121
122
```typescript { .api }
123
/**
124
* Register event handler for channel messages
125
* @param event - Event name to listen for
126
* @param callback - Function to call when event is received
127
* @returns Reference number for removing handler
128
*/
129
on(event: string, callback: (response?: any) => void | Promise<void>): number;
130
131
/**
132
* Remove event handler
133
* @param event - Event name
134
* @param ref - Handler reference number (optional)
135
*/
136
off(event: string, ref?: number): void;
137
```
138
139
**Usage Example:**
140
141
```typescript
142
// Listen for messages
143
const messageHandler = channel.on("new_message", (payload) => {
144
console.log("New message:", payload.body);
145
console.log("From user:", payload.user);
146
});
147
148
// Listen for user events
149
const joinHandler = channel.on("user_joined", async (payload) => {
150
console.log("User joined:", payload.user.name);
151
await updateUserList(payload.user);
152
});
153
154
// Remove specific handler
155
channel.off("new_message", messageHandler);
156
157
// Remove all handlers for an event
158
channel.off("user_joined");
159
```
160
161
### Message Sending
162
163
Send messages to the channel with status handling.
164
165
```typescript { .api }
166
/**
167
* Send message to channel
168
* @param event - Event name
169
* @param payload - Message payload object
170
* @param timeout - Send timeout in milliseconds (optional)
171
* @returns Push instance for handling response
172
*/
173
push(event: string, payload: object, timeout?: number): Push;
174
```
175
176
**Usage Example:**
177
178
```typescript
179
// Send basic message
180
channel.push("new_message", {
181
body: "Hello, world!",
182
user_id: 123
183
})
184
.receive("ok", (response) => {
185
console.log("Message sent:", response);
186
})
187
.receive("error", (reasons) => {
188
console.error("Send failed:", reasons);
189
});
190
191
// Send with timeout
192
channel.push("file_upload", {
193
filename: "document.pdf",
194
size: 1024000
195
}, 10000) // 10 second timeout
196
.receive("ok", ({ upload_url }) => {
197
console.log("Upload URL received:", upload_url);
198
})
199
.receive("timeout", () => {
200
console.warn("Upload request timed out");
201
});
202
203
// Send and chain multiple status handlers
204
channel.push("update_profile", {
205
name: "John Doe",
206
email: "john@example.com"
207
})
208
.receive("ok", (profile) => {
209
console.log("Profile updated:", profile);
210
showSuccessMessage("Profile saved!");
211
})
212
.receive("error", ({ errors }) => {
213
console.error("Validation errors:", errors);
214
showErrorMessage(errors);
215
})
216
.receive("timeout", () => {
217
showWarningMessage("Request timed out, please try again");
218
});
219
```
220
221
### Lifecycle Event Handlers
222
223
Register handlers for channel lifecycle events.
224
225
```typescript { .api }
226
/**
227
* Register callback for channel close events
228
* @param callback - Function to call when channel closes
229
* @returns Reference number for removing handler
230
*/
231
onClose(callback: (payload: any, ref: any, joinRef: any) => void | Promise<void>): number;
232
233
/**
234
* Register callback for channel error events
235
* @param callback - Function to call when channel errors occur
236
* @returns Reference number for removing handler
237
*/
238
onError(callback: (reason?: any) => void | Promise<void>): number;
239
240
/**
241
* Handle incoming messages (low-level message processing)
242
* @param event - Event name
243
* @param payload - Message payload
244
* @param ref - Message reference
245
* @returns Processed message result
246
*/
247
onMessage(event: string, payload: any, ref: any): any;
248
```
249
250
**Usage Example:**
251
252
```typescript
253
// Handle channel close
254
const closeHandler = channel.onClose((payload, ref, joinRef) => {
255
console.log("Channel closed:", payload);
256
console.log("Close ref:", ref);
257
console.log("Join ref:", joinRef);
258
259
// Cleanup or redirect user
260
showMessage("Connection to room lost");
261
});
262
263
// Handle channel errors
264
const errorHandler = channel.onError(async (reason) => {
265
console.error("Channel error:", reason);
266
267
// Attempt to rejoin after error
268
try {
269
await new Promise(resolve => setTimeout(resolve, 1000));
270
channel.join();
271
} catch (error) {
272
console.error("Failed to rejoin channel:", error);
273
}
274
});
275
276
// Custom message processing
277
channel.onMessage = (event, payload, ref) => {
278
// Log all messages
279
console.log(`Message [${event}]:`, payload);
280
281
// Custom processing logic
282
if (event === "custom_event") {
283
return { processed: true, payload };
284
}
285
286
// Return original payload for default processing
287
return payload;
288
};
289
290
// Remove lifecycle handlers when done
291
channel.off("phx_close", closeHandler);
292
channel.off("phx_error", errorHandler);
293
```
294
295
## Advanced Usage Patterns
296
297
### Channel State Management
298
299
```typescript
300
// Monitor channel state changes
301
function monitorChannelState(channel: Channel) {
302
const checkState = () => {
303
switch (channel.state) {
304
case "joining":
305
console.log("Channel is joining...");
306
break;
307
case "joined":
308
console.log("Channel is active");
309
break;
310
case "leaving":
311
console.log("Channel is leaving...");
312
break;
313
case "closed":
314
console.log("Channel is closed");
315
break;
316
case "errored":
317
console.log("Channel has errors");
318
break;
319
}
320
};
321
322
// Check state periodically
323
const interval = setInterval(checkState, 1000);
324
325
// Cleanup
326
channel.onClose(() => clearInterval(interval));
327
}
328
```
329
330
### Error Recovery
331
332
```typescript
333
// Automatic rejoin on error with exponential backoff
334
function setupAutoRejoin(channel: Channel) {
335
let rejoinAttempts = 0;
336
const maxAttempts = 5;
337
338
channel.onError(async () => {
339
if (rejoinAttempts < maxAttempts) {
340
const delay = Math.pow(2, rejoinAttempts) * 1000; // Exponential backoff
341
rejoinAttempts++;
342
343
console.log(`Rejoining in ${delay}ms (attempt ${rejoinAttempts})`);
344
345
await new Promise(resolve => setTimeout(resolve, delay));
346
347
channel.join()
348
.receive("ok", () => {
349
rejoinAttempts = 0; // Reset on success
350
});
351
} else {
352
console.error("Max rejoin attempts exceeded");
353
}
354
});
355
}