Getting Started With Node.js and WebSockets

Getting Started With Node.js and WebSockets

  • 833

Getting Started With Node.js and WebSockets .WebSockets are the edgy younger sibling of http. They let you maintain an _open_ connection for real time updates. Let’s make a simple chatroom that just displays messages from users. To keep this short, I won’t go into much besides the implementation,

WebSockets are the edgy younger sibling of http. They let you maintain an open connection for real time updates. Let’s make a simple chatroom that just displays messages from users. To keep this short, I won’t go into much besides the implementation, so if you need background info check out this explanation.

Overview

Were going to make a playground that lets 2+ tabs talk to each other in a little chat room.

This is image title

We’ll use two main files: server.js and index.html. We’re going to use an Express server to supply our index.html file, and we’re going to use the w[s](https://www.npmjs.com/package/ws) library for our second WebSocket server. Also, we’ll used vanilla JS on the frontend, since WebSockets is available in modern browsers; frameworks like React have nothing to do with it.

Installation

In a terminal, run:

mk websockets
cd websockets
> server.js 
> index.html
npm init -y
npm i express ws nodemon

Then add “start”: “nodemon server.js” to your package.json scripts.

Creating our main server

All it does is host our index.html:

const express = require('express');
const path = require('path');
const app = express();
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'index.html'));
});
const port = 8765;
app.listen(port, () => {
  console.log(`listening http://localhost:${port}`);
});

Remember, our eventual WebSockets server will be different than our main workhorse server. Our API and routes and assets would go in this server if this were a real project. We’ll get to the socket server in a second, but let’s start our index.html file:

<!DOCTYPE html>
<html lang="en">
  <title>Websocket Practice</title>
</head>
<body>
  <input id="name"/>
  <textarea id="message" ></textarea>
  <button>Send</button>
  <div id='messages'></div>
</body>
</html>

We’ll add in a <script> tag later, but for now we can do npm start and see that we have our page up and running.

Creating the WebSocket server

First off, let’s deal with our frontend clients making a connection. Back in our server.js file, throw this in:

const express = require('express');
const path = require('path');
const WebSocket = require('ws'); // new
const app = express();
// express code 
const socketServer = new WebSocket.Server({port: 3030});
socketServer.on('connection', (socketClient) => {
  console.log('connected');
  console.log('client Set length: ', socketServer.clients.size);
  socketClient.on('close', (socketClient) => {
    console.log('closed');
    console.log('Number of clients: ', socketServer.clients.size);
  });
});

What we’re doing is setting up ourWebSocketserver on port 3030. The connection event is fired when a frontend client hits this server and opens a steady connection. For now, all our WebSocket server does is log “connection” and then tells us how many client objects are currently connected to it. Also, heads up, the clients property is a Set, not an array.

Closing connections

We are also going to log every time a client connection is closed as well. This usually happens the browser is closed or refreshed. The important thing to note here is that client methods are nested inside the initial server connection event. WebSockets are open connections, so it makes sense that methods that deal with the client, ie the connections, would be nested.

Connecting from the client

With our socket server set up, we need to build our client. Since this project is so simple, I’m just going to put the JS in a script tag in our index.html file:

...
<div id='messages'></div>
<script>
const ws = new WebSocket('ws://localhost:3030');
ws.onopen = () => { 
  console.log('Now connected'); 
};
</script>
</body>
...

The new WebSocket(url) part is starting up a connection with our socket server. The most important thing to notice is that our url is using the ws protocol, not http (in the real world, always use the secure [wss](https://www.freecodecamp.org/news/how-to-secure-your-websocket-connections-d0be0996c556/)). And with that connection, we are assigning a method to its onopen method, which gets fired when a connection is successfully opened.

Testing it

Let’s run npm start and open up http://localhost:8765 in two tabs. In our node console we’ll see it log out the number of clients as 2. Try refreshing the tabs. The clients will be closed and reopened, but it won’t add any more. The only way to add additional clients would be to visit the url in more tabs.

Getting and sending messages from the server

The idea here is that each frontend client will send their message to the server, which will then send the message back to all the clients. Think of it like a relay system. We are also going to “save” those messages into an array, so that any new client who connects will be able to see the previous messages that were sent.

We only send the entire array of messages on the connection event, otherwise we only send individual messages:

// ...
const socketServer = new WebSocket.Server({port: 3030});
const messages = ['Start Chatting!'];
socketServer.on('connection', (socketClient) => {
  console.log('connected');
  console.log('Number of clients: ', socketServer.clients.size);
  socketClient.send(JSON.stringify(messages));
  
  socketClient.on('message', (message) => {
    messages.push(message);
    socketServer.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify([message]));
      }
    });
  });
  socketClient.on('close', (socketClient) => {
// ...

The main thing here is the new event message that we listen to on the socketClient. This event is fired each time the server recieves a message from the client. As mentioned, we want each message that hits the server from one client to then be sent to all the clients. That’s why we’re looping through the socketServer.clients set and using the send method to shoot off the message to each one. We are also making sure the connection is open as well.

Getting and sending messages from the client

Edit the index.html with this:

...
<button onClick="fire()">Send</button>
<div id="messages"></div>
<script>
const getElement = (id) => document.getElementById(id);
const addMessage = (message) => {
  const pTag = document.createElement('p');
  pTag.appendChild(document.createTextNode(message));
  getElement('messages').appendChild(pTag);
};
const ws = new WebSocket('ws://localhost:3030');
ws.onopen = () => { 
  console.log('Now connected'); 
};
ws.onmessage = (event) => {
  const messages = JSON.parse(event.data);
  messages.forEach(addMessage);
};
const fire = () => {
  const username = getElement('name').value || '???'
  ws.send(`${username}: ${getElement('message').value}`);
  getElement('message').value = '';
};
</script>

The first two new methods are helpers: getElement is an alias for document.getElementById, and addMessage handles adding messages to <p> tags. The crucial methods are onmessage and fire.

onmessage

This is the WebSocket function that fires when our client recieves a new message from the server. This is how our page will automatically update, it’s our listener. All we’re doing is reaching into the event object and JSON parsing the data. Now, in our case, we’re sending data in an array, that’s why we are using forEach, but you dont have to send the data like that.

fire

The magic is on the second line, ws.send(string), that’s the socket method that shoots our string data to the server. The other parts are just getting the value from the form and cleaning up our input (and don’t forget to add this method to the button).

Start playing around!

You’re done! You’ve got enough code to really start learning now. Add logs, play with parameters, and read some docs. Start exploring and have fun!