WebSockets
Agents support WebSocket connections for real-time, bi-directional communication. This page covers server-side WebSocket handling. For client-side connection, refer to the Client SDK.
Define onConnect and onMessage methods on your Agent to accept WebSocket connections:
import { Agent, Connection, ConnectionContext, WSMessage } from "agents";
export class ChatAgent extends Agent { async onConnect(connection, ctx) { // Connections are automatically accepted // Access the original request for auth, headers, cookies const url = new URL(ctx.request.url); const token = url.searchParams.get("token");
if (!token) { connection.close(4001, "Unauthorized"); return; }
// Store user info on this connection connection.setState({ authenticated: true }); }
async onMessage(connection, message) { if (typeof message === "string") { // Handle text message const data = JSON.parse(message); connection.send(JSON.stringify({ received: data })); } }}import { Agent, Connection, ConnectionContext, WSMessage } from "agents";
export class ChatAgent extends Agent { async onConnect(connection: Connection, ctx: ConnectionContext) { // Connections are automatically accepted // Access the original request for auth, headers, cookies const url = new URL(ctx.request.url); const token = url.searchParams.get("token");
if (!token) { connection.close(4001, "Unauthorized"); return; }
// Store user info on this connection connection.setState({ authenticated: true }); }
async onMessage(connection: Connection, message: WSMessage) { if (typeof message === "string") { // Handle text message const data = JSON.parse(message); connection.send(JSON.stringify({ received: data })); } }}Each connected client has a unique Connection object:
| Property/Method | Type | Description |
|---|---|---|
id | string | Unique identifier for this connection |
state | State | Per-connection state object |
setState(state) | void | Update connection state |
send(message) | void | Send message to this client |
close(code?, reason?) | void | Close the connection |
Store data specific to each connection (user info, preferences, etc.):
export class ChatAgent extends Agent { async onConnect(connection, ctx) { const userId = new URL(ctx.request.url).searchParams.get("userId");
connection.setState({ userId: userId || "anonymous", role: "user", joinedAt: Date.now(), }); }
async onMessage(connection, message) { // Access connection-specific state console.log(`Message from ${connection.state.userId}`); }}interface ConnectionState { userId: string; role: "admin" | "user"; joinedAt: number;}
export class ChatAgent extends Agent { async onConnect( connection: Connection<ConnectionState>, ctx: ConnectionContext, ) { const userId = new URL(ctx.request.url).searchParams.get("userId");
connection.setState({ userId: userId || "anonymous", role: "user", joinedAt: Date.now(), }); }
async onMessage(connection: Connection<ConnectionState>, message: WSMessage) { // Access connection-specific state console.log(`Message from ${connection.state.userId}`); }}Use this.broadcast() to send a message to all connected clients:
export class ChatAgent extends Agent { async onMessage(connection, message) { // Broadcast to all connected clients this.broadcast( JSON.stringify({ from: connection.id, message: message, timestamp: Date.now(), }), ); }
// Broadcast from any method async notifyAll(event, data) { this.broadcast(JSON.stringify({ event, data })); }}export class ChatAgent extends Agent { async onMessage(connection: Connection, message: WSMessage) { // Broadcast to all connected clients this.broadcast( JSON.stringify({ from: connection.id, message: message, timestamp: Date.now(), }), ); }
// Broadcast from any method async notifyAll(event: string, data: unknown) { this.broadcast(JSON.stringify({ event, data })); }}Messages can be strings or binary (ArrayBuffer / ArrayBufferView):
export class FileAgent extends Agent { async onMessage(connection, message) { if (message instanceof ArrayBuffer) { // Handle binary upload const bytes = new Uint8Array(message); await this.processFile(bytes); connection.send( JSON.stringify({ status: "received", size: bytes.length }), ); } else if (typeof message === "string") { // Handle text command const command = JSON.parse(message); // ... } }}export class FileAgent extends Agent { async onMessage(connection: Connection, message: WSMessage) { if (message instanceof ArrayBuffer) { // Handle binary upload const bytes = new Uint8Array(message); await this.processFile(bytes); connection.send( JSON.stringify({ status: "received", size: bytes.length }), ); } else if (typeof message === "string") { // Handle text command const command = JSON.parse(message); // ... } }}Handle connection errors and disconnections:
export class ChatAgent extends Agent { async onError(connection, error) { console.error(`Connection ${connection.id} error:`, error); // Clean up any resources for this connection }
async onClose(connection, code, reason, wasClean) { console.log(`Connection ${connection.id} closed: ${code} ${reason}`);
// Notify other clients this.broadcast( JSON.stringify({ event: "user-left", userId: connection.state?.userId, }), ); }}export class ChatAgent extends Agent { async onError(connection: Connection, error: unknown) { console.error(`Connection ${connection.id} error:`, error); // Clean up any resources for this connection }
async onClose( connection: Connection, code: number, reason: string, wasClean: boolean, ) { console.log(`Connection ${connection.id} closed: ${code} ${reason}`);
// Notify other clients this.broadcast( JSON.stringify({ event: "user-left", userId: connection.state?.userId, }), ); }}| Type | Description |
|---|---|
string | Text message (typically JSON) |
ArrayBuffer | Binary data |
ArrayBufferView | Typed array view of binary data |
For browser connections, use the Agents client SDK:
- Vanilla JS:
AgentClientfromagents/client - React:
useAgenthook fromagents/react
Refer to Client SDK for full documentation.
State synchronization Sync state between agents and clients.
Callable methods RPC over WebSockets for method calls.
Cross-domain authentication Secure WebSocket connections across domains.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2026 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-