WebSockets is a communications protocol that allows bidirectional, persistent communication as an alternative to HTTP. Suppose you wanted to get updates to some server state. With HTTP, you would need to poll frequently for the data.
But there are limitations to HTTP polling:
With WebSockets, clients don’t need to ask the server for new data, the web sockets server simply pushes the new data directly to the client.
WebSockets should be used when you need real-time functionality in your application — for example, a chat application or a bot that watches the stock market. WebSockets are best used in situations where you need to react quickly to frequently changing data. If the your application data doesn’t change often, it may be best to implement simple polling logic instead.
WebSockets use HTTP as the mechanism to initiate a connection to the server. This connection is then upgraded to a WebSocket connection.
Clients can only access WebSocket servers through a URI scheme of either ws:// or wss://. To initiate a WebSockets connection, you must first implement a WebSocket client and have a supporting WebSockets server. That’s where Deno comes in.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
Here’s what we’ll cover in this tutorial:
First, we’ll need Deno installed on our local machines. Once that’s done, create a new directory to house both our server and client code:
mkdir websocket-tutorial
Create a new file called server.ts. This is where we’ll create a simple HTTP server.
touch ./server.ts
Next, at the top of the file, import the serve function:
import { serve } from "https://deno.land/[email protected]/http/server.ts";
Now let’s create the HTTP server on port 80, which will be used to bootstrap, accept, and send WebSocket connections:
for await (const req of serve({port:80})){
req.response({body:"Hello world"})
}
serve() returns an async iterable consisting of HTTP requests made to our server. The for await syntax is used to loop through and respond to each request.
Run the code with the following command:
deno run --allow-net server.ts
The --allow-net parameter gives Deno permission to make network calls. Type localhost:80 in your browser and you should see a “Hello world” message on the page.
Next, we need to upgrade the regular HTTP request to a WebSocket connection. Import the acceptWebSocket function from Deno’s WebSocket module:
import {acceptWebSocket} from "https://deno.land/[email protected]/ws/mod.ts"
acceptWebSocket takes care of upgrading the request to a WebSocket connection. We’ll provide it with the necessary parameters:
for await (const req of serve({ port: 80 })) {
const { conn, r: bufReader, w: bufWriter, headers } = req;
acceptWebSocket({
conn,
bufReader,
bufWriter,
headers,
}).then(handleWs)
}
We haven’t created the handleWs function yet, so let’s do that before we move on:
async function handleWs(sock:WebSocket){
console.log("socket connected")
for await (const event of sock){
if(typeof event === "string"{
console.log(ev)
}
}
}
The handleWs function takes a WebSocket object as parameter. This object is an async iterable consisting of events emitted by the WebSocket-connected client. If the event is a string, the event is the message payload from the WebSocket client.
There are other kinds of events, which we’ll cover later. WebSockets can only transmit messages as an ArrayBuffer or a string, so you’ll need to use JSON.stringify for transmitting JSON messages.
With that done, let’s create the WebSocket client.
Create a new file in the project folder named client.ts:
touch ./client.ts
We’ll define a function called createWebSocket, which will contain the code for initializing a WebSocket and sending and receiving WebSocket messages.
function createWebSocket() {
const websocket = new WebSocket("ws://localhost:80")
websocket.onopen = () => {
setInterval(() => {
websocket.send(`Client says hello`)
}, 2000)
}
}
Websocket URLs start with wss:// for secure connections or ws:// for unsecure connections.
When a WebSocket object is newly created, its connection isn’t immediately ready. Harnessing WebSockets’ event-driven nature. we can attach a function to the WebSocket.onopen event listener. This function is called once the WebSocket connection is open.
Inside the onopen event listener function, we use the setInterval function to send a message every two seconds.
Let’s test out our code by starting up the WebSocket server:
deno run --allow-net server.ts
And the client:
deno run --allow-net client.ts
We should see “Client says hello” printed on the server console every two seconds.
We’ve seen how to send messages from the client to the server. But as we noted above, WebSockets allow bidirectional messaging. Let’s now send messages from the server to the client.
Update the handleWs function in server.ts:
async function handleWs(sock: WebSocket) {
if (!sock.isClosed) {
sock.send("Hi from server")
} //add this
for await (const ev of sock) {
if (typeof ev === "string") {
console.log(ev);
}
}
}
Notice that there’s a check to detect whether the socket has already been closed using the sock.isClosed property.
Update the createWebSocket function in client.ts to receive messages from the server:
function createWebSocket() {
const websocket = new WebSocket("ws://localhost:80")
websocket.onopen = () => {
setInterval(() => {
websocket.send(`Client says hello`)
}, 2000)
}
websocket.onmessage = (message) => {
console.log(message.data)
}
}
Running the updated code should show “Hi from server” on the client’s console.
The onmessage event listener function is used to subscribe to messages sent from the WebSocket server. Its payload is within the data property.
We’ve seen how to send messages from the server to a single client, but this is rarely useful; in a chat application, for example, you would need to broadcast a message to multiple clients instantly. You might also need to differentiate one WebSocket connection from another.
At the top of our server.ts file, we’ll create a Map object and assign to it the variable sockets:
const sockets = new Map<string, WebSocket>()
A Map is an object with methods that wrap a key-value pair. In this case, we associate a string to a WebSockets object. We will store all WebSockets connections in the sockets variable.
Now update the handleWs function:
async function handleWs(sock: WebSocket) {
console.log('connected')
const uid = v4.generate()
sockets.set(uid, sock)
for await (const ev of sock) {
if (isWebSocketCloseEvent(ev)) {
sockets.delete(uid)
return
}
if (typeof ev === "string") {
console.log(ev)
broadcastMessage(ev,uid)
}
}
}
Import the v4.generate function from the uuid library:
import { v4 } from 'https://deno.land/std/uuid/mod.ts';
The v4.generate function generates a random ID for each WebSocket connection. Each ID is used to identify a WebSocket connection when it sends a message. We’ll add this connection to the sockets variable.
Notice that we remove WebSocket connections once the close event occurs:
if (isWebSocketCloseEvent(ev)) {
sockets.delete(uid)
return
}
Next, we’ll create a function called broadcastMessage, which takes a message as a parameter for transmission to all the WebSockets stored in the sockets variable:
function broadcastMessage(message: string, uid: string) {
sockets.forEach((socket) => {
if (!socket.isClosed && uid !== id)
socket.send(message)
})
}
Notice we use a second parameter, uid, to identify the client sending the message. Notice the check uid !==id, which ensures we don’t broadcast a message to its source socket.
Now let’s update our client.ts file to simulate multiple clients connecting and sending messages. Update the createWebsocket function to take an id parameter:
function createWebSocket(id: number) {
const websocket = new WebSocket("ws://localhost:80")
websocket.onopen = () => {
setInterval(() => {
websocket.send(`Client ${id} says hello`)
}, 2000 * id)
}
websocket.onmessage = (message) => {
console.log(`Client ${id}: ${message.data}`)
}
}
Notice the setInterval argument value 2000 * id. Since we’re passing numbers as IDs, an ID of 1 would wait 2,000ms (i.e., 2s) to send a message, whereas an ID of 4 would wait 8,000ms (i.e., 8s).
We create multiple WebSocket connections using a for loop:
for (let x = 1; x < 10; x++) {
createWebSocket(x)
}
When we run both server.ts and client.ts, we should see messages like this:
Client 3: Client 8 says hello Client 4: Client 8 says hello Client 6: Client 8 says hello
The WebSockets protocol provides a way to communicate bidirectionally without polling. WebSockets should be used for real-time applications like stock market visualization and messaging apps that require instant feedback.
Using WebSockets for applications with data that doesn’t change often would be overkill, and as such, a simple HTTP polling mechanism would be preferable. The full code for this tutorial can be found in the repo here.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

:has(), with examplesThe CSS :has() pseudo-class is a powerful new feature that lets you style parents, siblings, and more – writing cleaner, more dynamic CSS with less JavaScript.

Kombai AI converts Figma designs into clean, responsive frontend code. It helps developers build production-ready UIs faster while keeping design accuracy and code quality intact.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the October 22nd issue.

John Reilly discusses how software development has been changed by the innovations of AI: both the positives and the negatives.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now