Using WebSockets
WebSockets allow you to communicate in real time with your Cloudflare Workers serverless functions. In this guide, you will learn the basics of WebSockets on Cloudflare Workers, both from the perspective of writing WebSocket servers in your Workers functions, as well as connecting to and working with those WebSocket servers as a client.
WebSockets are open connections sustained between the client and the origin server. Inside a WebSocket connection, the client and the origin can pass data back and forth without having to reestablish sessions. This makes exchanging data within a WebSocket connection fast. WebSockets are often used for real-time applications such as live chat and gaming.
Writing a WebSocket Server
WebSocket servers in Cloudflare Workers allow you to receive messages from a client in real time. This guide will show you how to set up a WebSocket server in Workers.
A client can make a WebSocket request in the browser by instantiating a new instance of WebSocket
, passing in the URL for your Workers function:
// In client-side JavaScript, connect to your Workers function using WebSockets:
const websocket = new WebSocket('wss://example-websocket.signalnerve.workers.dev');
When an incoming WebSocket request reaches the Workers function, it will contain an Upgrade
header, set to the string value websocket
. Check for this header before continuing to instantiate a WebSocket:
async function handleRequest(request) { const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Expected Upgrade: websocket', { status: 426 }); }
}
After you have appropriately checked for the Upgrade
header, you can create a new instance of WebSocketPair
, which contains server and client WebSockets. One of these WebSockets should be handled by the Workers function and the other should be returned as part of a Response
with the 101
status code, indicating the request is switching protocols:
async function handleRequest(request) { const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Expected Upgrade: websocket', { status: 426 }); }
const webSocketPair = new WebSocketPair(); const client = webSocketPair[0], server = webSocketPair[1];
return new Response(null, { status: 101, webSocket: client, });
}
The WebSocketPair
constructor returns an Object, with the 0
and 1
keys each holding a WebSocket
instance as its value. It is common to grab the two WebSockets from this pair using Object.values
and ES6 destructuring, as seen in the below example.
In order to begin communicating with the client
WebSocket in your Worker, call accept
on the server
WebSocket. This will tell the Workers runtime that it should listen for WebSocket data and keep the connection open with your client
WebSocket:
async function handleRequest(request) { const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Expected Upgrade: websocket', { status: 426 }); }
const webSocketPair = new WebSocketPair(); const [client, server] = Object.values(webSocketPair);
server.accept();
return new Response(null, { status: 101, webSocket: client, });
}
WebSockets emit a number of Events that can be connected to using addEventListener
. The below example hooks into the message
event and emits a console.log
with the data from it:
async function handleRequest(request) { const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Expected Upgrade: websocket', { status: 426 }); }
const webSocketPair = new WebSocketPair(); const [client, server] = Object.values(webSocketPair);
server.accept(); server.addEventListener('message', event => { console.log(event.data); });
return new Response(null, { status: 101, webSocket: client, });
}
Connecting to the WebSocket server from a client
Writing WebSocket clients that communicate with your Workers function is a two-step process: first, create the WebSocket instance, and then attach event listeners to it:
const websocket = new WebSocket('wss://websocket-example.signalnerve.workers.dev');websocket.addEventListener('message', event => { console.log('Message received from server'); console.log(event.data);
});
WebSocket clients can send messages back to the server using the send
function:
websocket.send('MESSAGE');
When the WebSocket interaction is complete, the client can close the connection using close
:
websocket.close();
For an example of this in practice, refer to the websocket-template
to get started with WebSockets.
Writing a WebSocket client
A Worker can also establish a WebSocket connection to a remote server.
Cloudflare does not currently support the client implementation described above within a Worker. Instead, WebSockets are established by making a fetch request to a URL with the Upgrade
header set.
async function websocket(url) { // Make a fetch request including `Upgrade: websocket` header. // The Workers Runtime will automatically handle other requirements // of the WebSocket protocol, like the Sec-WebSocket-Key header. let resp = await fetch(url, { headers: { Upgrade: 'websocket', }, });
// If the WebSocket handshake completed successfully, then the // response has a `webSocket` property. let ws = resp.webSocket; if (!ws) { throw new Error("server didn't accept WebSocket"); }
// Call accept() to indicate that you'll be handling the socket here // in JavaScript, as opposed to returning it on to a client. ws.accept();
// Now you can send and receive messages like before. ws.send('hello'); ws.addEventListener('message', msg => { console.log(msg.data); });
}
Durable Objects and WebSocket state
If your application needs to coordinate among multiple WebSocket connections, such as a chat room or game match, you will need to create a Durable Object so clients send messages to a single-point-of-coordination. Durable Objects are a coordinated state tool for Cloudflare Workers, which are often used in parallel with WebSockets to persist state over multiple clients and connections. Refer to Durable Objects to get started.