WebSockets Events with Node.js

Uriel Rodriguez
6 min readDec 21, 2020

In the previous blog, Getting Started with WebSockets, I went over the initial configuration of the server set-up to change its behavior to allow bidirectional communication between it and the client. The configuration included the Socket.io library which handled the configuration of the server, priming the application to take advantage of websocket connections and real-time communication. Picking up from there, after the application is configured to maintain open and persistent connections between the server and client, we can begin to implement various communication between server and client, known as events. The first type of event is between the server and an individual client, for example when a client connects to the server by visiting a specific URL and sending a GET request to that endpoint. The server at this point can listen for that client connection and respond to the client but maintain that connection open as opposed to the HTTP protocol in which after the server sends its response, the connection to the client is closed. Let’s take a look at that implementation.

// index.js server-sideconst http = require('http');
const express = require('express');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
const port = process.env.PORT || 3000;// server listening for client connection
io.on('connection', socket => {
console.log('New websocket connection!');
});
server.listen(port, () => console.log(`Server is listening on port ${port}`));

Here we can see the implementation that closely resembles the HTTP protocol in which a client reaches out to a server and receives a response. The implementation shows that the reconfigured server, stored in the “io” variable calls a method “on” which receives two arguments, the first being what event the server will respond to, and how it will respond. The first argument, “connection” is the event the server is responding to, in this case, the connection of a client to the server. The second argument is a callback, that receives the “socket” object which contains information on the connection. After listening for a new connection, the server will simply log to the console.

Next, not only can the server perform some action server-side on an event, but can also send or emit an event to the client, to which the client must listen for. Once the connection is established and maintained with the client, the server can now communicate back and forth like so:

...// server listening for client connection
io.on('connection', socket => {
console.log('New websocket connection!');
socket.emit('message', 'Welcome!');
});
...

After a client makes a connection with the server, the server is able to send the client, or emit events to the client, but the client must listen for those events in order to process them. This is carried out using the “socket” object which represents the connection between the server and the client. Here the server is using the specific connection to the client, and emitting a “message” event. The “emit” method takes two arguments, the first is the name for the event the server will emit to the client, which can be anything that describes what you want to send the client. The second argument is the data that the server will send to the client. But as mentioned before, the client must also listen for these server events so that must configured. So:

// app.js client-sideconst socket = io();socket.on('message', serverMsg => {
console.log(serverMsg);
};

Very simply, the first thing needed is to gain access to the Socket.io library configuration which was loaded into the HTML via the script tag, refer back to my previous blog for implementation details. After this, we specify what the client will do upon an event “message”, which is to receive the incoming data from the server, which is assigned to the “serverMsg” variable, a variable of your choosing, and log it to the console.

At this point, the client is responding to a client connecting to it, and also sending the client some data, a welcome message. The client can also communicate with the server by emitting an event of their own, to which the server can listen for. Let’s implement that.

// app.js client-side...const form = document.querySelector('form');form.addEventListener('submit', event => {
event.preventDefault();
// there are many elements within the form
// grabbing the element with a "message" name property
// accessing that element's, an input field, value
const message = event.target.elements.message.value;
socket.emit('inputMessage', message);
});

In addition to the prior client-side code, we have now added a hypothetical situation in which the client would send the server some data. We created a form, attached an event listener to it, and captured an input element’s value within the “message” variable. After that, we “emit” this message via the “inputMessage” event to the server via the persisted connection with the server. Now the server must listen for this event in order to process it. So:

// index.js server-sideio.on('connection', socket => {
...
socket.on('inputMessage', clientMsg => {
console.log(clientMsg);
});
});

The syntax is very similar to when a server emits an event to the client, but here we can see that the server is listening for an “inputMessage” event from the client. And “on” that event, it will receive the data, the actual message, “clientMsg” and log it to the console.

Up to this point, we have seen how the server and a single client communicate with each other via events. But now let’s take a look at how the server can communicate with multiple clients, and those clients can communicate with each other via the server. The ability of the server to interact with multiple clients is made possible by the persistent connection that is made in the WebSocket protocol between the server and the client. One type of communication that can be carried out is called a broadcast, and that is when the server communicates to every client about something, except the client that established the new connection with the server. An example of this is seen within chat applications: when a new user joins a chat, every other user is notified that the new user has joined the chat, but the new user is not notified that it has joined the chat. The implementation of this type of communication is as follows:

// index.js server-sideio.on('connection', socket => {
...

socket.broadcast.emit('message', 'New user has joined!');
});

So again, every form of communication occurs within the body of the callback which represents the initial connection made with the client. When a new client makes a connection with the server, the server will “broadcast” and “emit” a “message” event to every client but the client that triggered the broadcast event. And on the client-side, the client is listening for a “message” event which we implemented earlier, and logs to the console the server message.

Finally, the last type of communication possible is the one between all clients and this is triggered upon a client emitting an event to the server. Once the server receives this event, it emits it to every client connected. An example of this is when a user sends a message, and that message is seen by every user in the chat. Implementing this is simple and will use code we have already written and refactor it a little.

// index.js server-sideio.on('connection', socket => {
...
socket.on('inputMessage', clientMsg => {
// previous implementation
console.log(clientMsg);
// inform all clients of incoming client message
io.emit('message', clientMsg);
});
});

All that was added was the line of code that ensures that the server not only logs the client message to the console but emits the message to every client. The client is already listening for a “message” event and will log it to the console according to the code written above.

And so with these few lines of code, both on the server-side and the client-side, we have enabled real-time bidirectional communication between server and client. You can also see the various types of real-time communication made possible by the WebSocket protocol and the Socket.io library. To learn more about the implementation, the Socket.io documentation provides demos and clear instructions on how to get to started.

--

--

Uriel Rodriguez

Flatiron School alumni and Full Stack web developer.