Build

Nodejs WebSocket Examples

6 min read Michael Carroll on Sep 21, 2022
Try PubNub Today

Free up to 1MM monthly messages. No credit card required.

Subscribe to our newsletter

By submitting this form, you are agreeing to our Terms and Conditions and Privacy Policy.

An overview and examples of basic socket and WebSocket programming with Node.js.

An introduction to WebSockets with Node.js and in plain Javascript

If you are reading this then you are probably interested in creating an application or service that needs two-way communication that can be initiated from either side (full duplex). WebSockets were designed and developed to tackle this very problem, and Node JS can be used to quickly develop and deploy a service to utilize this technology.

What is a Nodejs WebSocket?

According to Wikipedia: WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. What does that mean? It allows modern computers that already have the capabilities to communicate over TCP to now make a connection between two devices (server and client) where communication can be pushed from either direction.

When Should I Write Nodejs WebSocket Programming Code?

WebSockets create a TCP socket connection between multiple devices or processes. Similar functionality can be implemented using HTTP Long Polling or a service like PubNub. Let’s build a simple example demo to see what we can accomplish with Websockets using Node.js

Node.js WebSocket Server Example

There are lots of examples of how to create Node.js Socket servers. A lot of examples spend a lot of time explaining to the end user what happens with the communication after it is delivered.

If you are looking for a good video tutorial and some code to go along with it here is my colleague’s example: Node.js WebSocket Programming Tutorial with JavaScript

The code from the video is available in my colleague’s previous example: Node.js WebSocket Examples GitHub Repository.

For my example, I created a simple visual to show messages being delivered between two devices, almost like a virtual high-five. I called it a wishing well because it is a simple div box that when you select one of the simple “well wishes” and then click the div box, it will send that “well wish” to everyone else on the server, so a virtual way to wish well … I mean wishing well.

How do WebSockets Work in Node.js?

Luckily WebSockets are easy to install with Node.js and just as easy to use, with only a few lines of code. When we get to the client web browser, it will be even easier. Hint: we don’t have to install or download anything, modern browsers have this support built-in. Since I am also going to want to deliver a web page for my client to connect, I am going to install Express.js as well.

$ npm install --save express
$ npm install --save ws

I then created a simple HTML server using express to deliver the client page.

const express = require('express');
 
const server = express()
 .use((req, res) => res.sendFile('/index.html', { root: __dirname }))
 .listen(3000, () => console.log(`Listening on ${3000}`));

For the Websocket server, the first example will be the server periodically sending messages to the clients; every second announcing the server’s time; and then every 8 seconds it randomly selects a “well wish” to send and a location to display it to all the clients.

const { Server } = require('ws');
 
const sockserver = new Server({ port: 443 });
sockserver.on('connection', (ws) => {
   console.log('New client connected!'); 
   ws.on('close', () => console.log('Client has disconnected!'));
});
 
setInterval(() => {
   sockserver.clients.forEach((client) => {
       const data = JSON.stringify({'type': 'time', 'time': new Date().toTimeString()});
       client.send(data);
   });
}, 1000);
 
setInterval(() => {
   sockserver.clients.forEach((client) => {
       const messages = ['Hello', 'What do you ponder?', 'Thank you for your time', 'Be Mindful', 'Thank You'];
       const random = Math.floor(Math.random() * messages.length);
       let position = {x: Math.floor(Math.random() * 200), y: Math.floor(Math.random() * 150)}
       const data = JSON.stringify({'type': 'message', 'message': messages[random], 'position': position});
       client.send(data);
   });
}, 8000);

This first line is calling the ws package that we installed and is the Node.js WebSocket server (ws). To implement it we just initialize a variable with a port to run on. Websockets are designed to run on the same ports as HTTP (80 and 443), this is accomplished because Websockets use HTTP’s handshake to change over to the other protocol if needed.

sockserver.on('connection', (ws) => {
   console.log('New client connected!'); 
   ws.on('close', () => console.log('Client has disconnected!'));
});

The next lines of code are what to do when there is a new connection to the WebSocket server. My code simply displays a message on the terminal notifying that there is a new connection, and establishes a new event on that connection, to display the client disconnected when the connection is closed.

setInterval(() => {
   sockserver.clients.forEach((client) => {
       const data = JSON.stringify({'type': 'time', 'time': new Date().toTimeString()});
       client.send(data);
   });
}, 1000);

After that, both interval sections accomplish the same thing; sending a message to all the clients currently connected to the WebSocket server. This is accomplished by calling the WebSocket server’s variable and grabbing the clients array from it. You can then iterate through the client connections and then send JSON stringified data to each.

That’s it, we have a WebSocket server written using Node.js with minimal code. To run all we would need to do is run node with our saved file (index.js here):

$ node index.js
</code block>

How do I use WebSockets in the Web Browser?

As I mentioned before, luckily we don’t have to install any package to use WebSockets with modern web browsers. All we need to do is call the WebSocket Object with a URI as the parameter.

const webSocket = new WebSocket('ws://localhost:443/');

And then we can create an event for the WebSocket to do something when it gets data.

webSocket.onmessage = (event) => {
   const data = JSON.parse(event.data);
   console.log(data);
}

That’s it we can now receive data from the WebSocket server.

Here is my full example of the wishing well, displaying the “well wishes” and the server time on the webpage:

<html>
   <head>
   <style>
       #wishing-well {
           position: relative;
           width: 400px;
           height: 300px;
           margin: 50px;
           outline: solid 1px black;
       }
   </style>
   <script>
       const webSocket = new WebSocket('ws://localhost:443/');
       let el;
 
       function fade(element) {
           var op = 1;  // initial opacity
           var timer = setInterval(function () {
               if (op <= 0.1){
                   clearInterval(timer);
                   element.style.display = 'none';
               }
               element.style.opacity = op;
               element.style.filter = 'alpha(opacity=' + op * 100 + ")";
               op -= op * 0.1;
           }, 100);
       }
 
       function unfade(element) {
           var op = 0.1;  // initial opacity
           element.style.display = 'block';
           var timer = setInterval(function () {
               if (op >= 1){
                   clearInterval(timer);
               }
               element.style.opacity = op;
               element.style.filter = 'alpha(opacity=' + op * 100 + ")";
               op += op * 0.1;
           }, 100);
       }
 
       webSocket.onmessage = (event) => {
           const data = JSON.parse(event.data);
           if (data.type == 'time') {
               el = document.getElementById('time');
               el.innerHTML = 'Time on the Server: ' + data.time;
           }
           if (data.type == 'message') {
               // Grab the wishing-well
               const palette = document.getElementById('wishing-well');
 
               // create new div with position and innerHtml
               let msgContainer = document.createElement("div");
               msgContainer.id = "message"
               msgContainer.style.position = "absolute";
               console.log("position: ", data.position)
               msgContainer.style.top = data.position.y + "px";
               msgContainer.style.left = data.position.x + "px";
               msgContainer.style.opacity = 0;
               msgContainer.innerHTML = data.message;
 
               // add the new div to wishing-well
               palette.appendChild(msgContainer);
               unfade(msgContainer)
 
               // fire off fade/delete new div with a delay in the beginning
               async function fadeAndDelete() {
                   setTimeout(() => {
                       fade(msgContainer);
                       setTimeout(() => {
                           let elem = document.getElementById("message");
                           elem.remove();
                       }, 1100)
                   }, 4000);
               }
              
               fadeAndDelete();
           }
       };
   </script>
   </head>
   <body>
       <p id="time"></p>
       <div id="wishing-well"></div>
   </body>
</html>

When it is running it looks like this:

Node.js WebSocket Programming Examples

How do I change the server to send the messages it receives?

Having a server that can periodically send data to multiple clients with minimal code is great for applications where you need to stream data to end users, such as a stock ticker or a sports score update. But what if I wanted to have end users send messages to each other, how would that be accomplished with Websockets on a NodeJS server? We simply change the server from sending periodic messages to now only sending messages after it receives it from one of the WebSockets.

Here is what that code looks like now:

const { Server } = require('ws');
 
const sockserver = new Server({ port: 443 });
const connections = new Set();
sockserver.on('connection', (ws) => {
   console.log('New client connected!');
   connections.add(ws)
   ws.on('message', (data) => {
       const message = JSON.parse(data);
       connections.forEach((client) => {
           client.send(JSON.stringify(message));
       })
   });
 
   ws.on('close', () => {
       connections.delete(ws);
       console.log('Client has disconnected!');
   });
});

So we added a new event to the WebSocket connection when it is connected. It is the message event, and when that happens we take the data we received from the WebSocket connection and cycle through all the connections sending the data to each of them. I also added the connections to a Set to iterate through. I remove the connection from the set when it is closed. Now the WebSocket server can receive and send messages, so it is now an intermediary for end users to send messages to each other. This server has no user management or a concept of rooms, but it is little more than a group chat where users can only send “well wishes” to each other.

How do I change the client to send messages?

Once again on the client side, it is very easy to send data. We can call the send function and send JSON stringified data with the WebSocket we declared earlier.

const webSocket = new WebSocket('ws://localhost:443/');
const data = {'type': 'message', 'message': msg.value, 'position': position};
 webSocket.send(JSON.stringify(data));

Most of the changes occur with the UI/UX part of the code.

Here is the full code to try it out.

<html>
   <head>
   <style>
       #wishing-well {
           position: relative;
           width: 400px;
           height: 300px;
           margin: 50px;
           outline: solid 1px black;
       }
       div.btn {
           display: inline-block;
           border: 2px solid #ccc;
           margin-right: 5px;
           padding: 2px 5px;
           cursor: pointer;
       }
       div.btn.on {
           background-color: #777;
           color: white;
       }
   </style>
   <script>
       const webSocket = new WebSocket('ws://localhost:443/');
 
       window.onload = function() {
           let boxes = document.getElementsByClassName('btn');
           for(let i = 0; i < boxes.length; i++) {
               let box = boxes[i];
               box.onclick = function(e) {
                   for(let j = 0; j < boxes.length; j++) {
                       let b = boxes[j];
                       b.classList.remove('on');
                   }
                   e.target.classList.add('on');
                   document.getElementById('wishing-well-message').value = e.target.innerHTML;
               }
           }
 
           document.getElementById("wishing-well").onclick = function (e) {
               let rect = e.target.getBoundingClientRect();
               let x = e.clientX - rect.left;
               let y = e.clientY - rect.top;
               const msg = document.getElementById('wishing-well-message');
               if (msg.value !== null && msg.value != "") {
                   let position = {x, y}
                   const data = {'type': 'message', 'message': msg.value, 'position': position};
                   webSocket.send(JSON.stringify(data));
                   console.log(JSON.stringify(data));
               }
           }
       }
 
       function fade(element) {
           var op = 1;  // initial opacity
           var timer = setInterval(function () {
               if (op <= 0.1){
                   clearInterval(timer);
                   element.style.display = 'none';
               }
               element.style.opacity = op;
               element.style.filter = 'alpha(opacity=' + op * 100 + ")";
               op -= op * 0.1;
           }, 100);
       }
 
       function unfade(element) {
           var op = 0.1;  // initial opacity
           element.style.display = 'block';
           var timer = setInterval(function () {
               if (op >= 1){
                   clearInterval(timer);
               }
               element.style.opacity = op;
               element.style.filter = 'alpha(opacity=' + op * 100 + ")";
               op += op * 0.1;
           }, 100);
       }
 
       webSocket.onmessage = (event) => {
           const data = JSON.parse(event.data);
           let el;
           if (data.type == 'time') {
               el = document.getElementById('time');
               el.innerHTML = 'Time on the Server: ' + data.time;
           }
           if (data.type == 'message') {
               // Grab the wishing-well
               const palette = document.getElementById('wishing-well');
 
               // create new div with position and innerHtml
               let msgContainer = document.createElement("div");
               msgContainer.id = "message"
               msgContainer.style.position = "absolute";
               console.log("position: ", data.position)
               msgContainer.style.top = data.position.y + "px";
               msgContainer.style.left = data.position.x + "px";
               msgContainer.style.opacity = 0;
               msgContainer.innerHTML = data.message;
 
               // add the new div to wishing-well
               palette.appendChild(msgContainer);
               unfade(msgContainer)
 
               // fire off fade/delete new div with a delay in the beginning
               async function fadeAndDelete() {
                   setTimeout(() => {
                       fade(msgContainer);
                       setTimeout(() => {
                           let elem = document.getElementById("message");
                           elem.remove();
                       }, 1100)
                   }, 4000);
               }
              
               fadeAndDelete();
           }
       };
  
 
   </script>
   </head>
   <body>
       <p id="time"></p>
       <div id="wishing-well"></div>
       <div class="button-row">
           <input type="hidden" id="wishing-well-message">
           <div class="btn">Hello</div>
           <div class="btn">What do you ponder?</div>
           <div class="btn">Thank you for your time</div>
           <div class="btn">Be Mindful</div>
           <div class="btn">Thank You</div>
       </div>
   </body>
</html>

And this is what it looks like:

WebSocket programming with Node.js

So what’s left?

As a software developer, this project is finished, but for it to be useful it would need to be running on a server somewhere. That server would need to be maintained and fault protected to run even if it were to crash for some reason (which would be very easy since we didn’t write any error handling). But with cloud providers, you can easily deploy servers to get started.

Another area where our server is lacking is client identification and separating/filtering messages depending on certain circumstances (e.g. sending a message so that is only delivered to 1 identified person aka a Direct Message)

These are all complex problems that deserve blog entries of their own. But there are cloud solutions out there that can make these problems much easier to solve. One of those solutions is PubNub.

How can PubNub Solve These Problems?

PubNub offers the ability to do this bi-directional communication through its servers. Since it is globally distributed and a scalable cloud service, we don’t have to worry about deploying and maintaining servers. And the SDKs have the ability to identify users and send messages to specific channels that only users subscribed to those channels will receive.

So now to change my “wishing well”, I now no longer need to host a WebSocket server and can deliver a static page I can upload to any server to deliver.

The first change would be to remove the WebSocket and initialize a PubNub Connection.

window.onload = function() {
       let pubnub = new PubNub({
           publishKey : ${YOUR PubNub PUBLISH KEY},
           subscribeKey : ${YOUR PubNub SUBSCRIBE KEY},
           uuid: ${SOME RANDOM ID}
       });
const listener = {
           message: (data) => {
               showMessage(data)
           }
       }
       pubnub.addListener(listener);
       pubnub.subscribe({
           channels: ["wishing-well"]
       })
 }

What this code does is initialize the PubNub SDK and sets up a listener which when it receives a message calls showMessage which does what our previous example did when it received data. The subscribe call is the actual call to make a connection to PubNub, it will subscribe to the “wishing-well” channel and any data sent to that channel will be forwarded to this client.

And then to send the “well wish” to the other clients we just publish that message to the same channel.

pubnub.publish({
    message: msg.value,
    meta: position,
    channel: 'wishing-well'
})

Now we can run our same wishing well on any service that lets me upload a static file, and since there are no servers needed to run the connections the page will always work.

And that’s all there is to it! For more on implementing JavaScript with PubNub, take a look at our React Native blog post tutorials for real-time geolocation tracking, a real-time chat app, and more! There is also a blog post about socket programming in Python. Thanks for reading! If you liked this post, subscribe to our newsletter!

More from PubNub

Java WebSocket Programming with Android and Spring Boot
BuildNov 18, 20225 min read

Java WebSocket Programming with Android and Spring Boot

An overview and examples of Java WebSocket programming in Android and Spring Boot.

Michael Carroll

Michael Carroll

NPP and HIPAA: Notice of Privacy Practices Definition
Healthcare CategoryNov 15, 20225 min read

NPP and HIPAA: Notice of Privacy Practices Definition

A Notice of Privacy Practices (NPP) is one of the requirements of HIPAA and helps patients understand their personal data rights.

Michael Carroll

Michael Carroll

How Fast is Real-Time? Human Perception and Technology
InsightsNov 13, 20226 min read

How Fast is Real-Time? Human Perception and Technology

Just how fast is this concept of 'real-time?' How fast can the human mind process input? What about emulating that with...

Michael Carroll

Michael Carroll