DEV Community

Cover image for 👓💻 How to set up Websocket connections between nodejs services
Michael "lampe" Lazarski
Michael "lampe" Lazarski

Posted on

👓💻 How to set up Websocket connections between nodejs services

Most of the tutorials and lessons on the internet are about how to connect your browser with a server via WebSockets. In this blog post, I want to show you that you can also use WebSockets to connect servers with each other and on the way also explain it a little bit.

What are WebSockets?

WebSockets are a bi-directional connection and a communications protocol initiated over HTTP.

This means that both the server and the client can send and react to a message and talk to each other without creating a new connection since WebSockets are always stay connected.

Websockets need an HTTP connection to tell both that you want to upgrade to WebSockets. The connection then will be upgraded to WebSockets. You can see this because all the WebSocket connection have a ws:// instead of an http:// in the URL.

Websocket connections also support a secure connection which you should use in production. You can see this like in HTTP you just need to add an s. http would be https and ws would be wss. You should also not mix secure with insecure connections. In general, always secure everything.

What we will do in this small tutorial is to connect three services with each other and send a message between them. We will also add colourful logging to make it easier to read the logs. We will also use nodemon for easier development.

The Setup

Let us first create our project.

mkdir websockets-tutorial
cd websockets-tutorial
npm init -y
git init
Enter fullscreen mode Exit fullscreen mode

Now, let us add the packages we will be using and create the files we need.

npm i -s chalk nodemon socket.io socket.io-client
touch service1.js service2.js service3.js
Enter fullscreen mode Exit fullscreen mode

The next step is to create our npm scripts.
Open your package.json and add the following scripts:

  "scripts": {
    "start": "npm run service1 & npm run service2 & npm run service3",
    "service1": "nodemon service1.js",
    "service2": "nodemon service2.js",
    "service3": "nodemon service3.js"
  },
Enter fullscreen mode Exit fullscreen mode

The implimantion

Let us first write a log function that will output colourful logs.
We need to import chalk first.

const chalk = require('chalk');
Enter fullscreen mode Exit fullscreen mode

and then we can implement a simple log function.

const log = (msg) => console.log(chalk.bold.green.bgBlack('Service1:'), chalk.white.bgBlack(`${msg}`))
Enter fullscreen mode Exit fullscreen mode

We will use the same function in every file and we need to change a few things. The following part of the code needs to be changed in the place where I have added [ ] around it.

chalk.bold.[green].bgBlack('Service[1]:')
Enter fullscreen mode Exit fullscreen mode

[green] I would take another colour for every service and of course the [1] needs to be changed to the number.

The WebSocket server

First let us import the packages we need.

const server = require('http').createServer();
const io = require('socket.io')(server, {
    path: '/'
});
Enter fullscreen mode Exit fullscreen mode

Here we are importing the http server from node and passing it to socket.io.
In socket.io we need to set the path the server should listen to for incoming connections. In our case, it is the root path /.

At the end of your file, add the following code to make the server listen to a port.

server.listen(4000);
log('started server on port 4000')
Enter fullscreen mode Exit fullscreen mode

In service1 the port will be 4000 and in service2 it will be port 5000 and in service3 it will be the port 6000.

Okay now, let us implement the actual communication.

io.on('connection', client => {
    log('someone connected');

    client.on('sayHello', data => {
        log(`event: sayHello, data: ${data}`)
        client.emit('halloToYouTo', 'Hello from http://localhost:4000')
    });

    client.on('disconnect', () => {
        log('client disconnected')
    });
});
Enter fullscreen mode Exit fullscreen mode

Okay, we are dealing with WebSockets. Websockets are always connected so we need to react to things happening. This is why you will see .on() and .emit().
.on() shows you that the function will run when something will happen.
For example: io.on('connection', someFunction);. You can translate in your head to: "When someone connected to our WebSocket the someFunction will be run".
Inside of that we have the following code client.on('sayHello', sayHelloFunction). The sayHelloFunction will be executed once a client has emitted the 'SayHello' event. We then .emit() the 'HalloToYouTo' event. This will be then sent to all open connections. Then you should already understand what disconnect will do, right?

Here is the full code of the service.js file

const chalk = require('chalk');
const server = require('http').createServer();
const io = require('socket.io')(server, {
    path: '/'
});

const log = (msg) => console.log(chalk.bold.green.bgBlack('Service1:'), chalk.white.bgBlack(`${msg}`))

io.on('connection', client => {
    log('someone connected');

    client.on('sayHello', data => {
        log(`event: sayHello, data: ${data}`)
        client.emit('halloToYouTo', 'Hello from http://localhost:4000')
    });

    client.on('disconnect', () => {
        log('client disconnected')
    });
});

server.listen(4000);
log('started server on port 4000')
Enter fullscreen mode Exit fullscreen mode

Lets now go to implement servie2.js.

const chalk = require('chalk')
const server = require('http').createServer();
const ioServer = require('socket.io')(server, {
    path: '/'
});
const ioClient = require('socket.io-client');

const log = (msg) => console.log(chalk.bold.cyan.bgBlack('Service2:'), chalk.white.bgBlack(`${msg}`))
Enter fullscreen mode Exit fullscreen mode

So until here, the code looks like in service1.js. The only change is that we are importing now the socket.io-client as ioClient.

Next we will setup a server again

// Server Code
ioServer.on('connection', client => {
    log('someone connected');
    client.on('sayHello', data => {
        log(`event: sayHello, data: ${data}`)
        client.emit('halloToYouTo', "hello from http://localhost:5000")
    });
    client.on('disconnect', () => {
        log('event: disconnect, client disconnected')
    });
});
Enter fullscreen mode Exit fullscreen mode

This is the same code as in service1.js.
Now let's implement the client. First we need to initalize the client.

// Client Code
const serverAddr = 'http://localhost:4000';
const socket = ioClient(serverAddr, {
    path: '/'
});
Enter fullscreen mode Exit fullscreen mode

The serverAddr must be the address of the server we want to connect. In our case, this is service1 with the address http://localhost:4000. Remember we need HTTP to connect, but after the first handshake, the connection will be upgraded to a WebSocket connection. Now that we have that setup, we can now connect and set up the events we want to react to.

socket.on('connect', (data) => {
    log(`connected to ${serverAddr}`);

    socket.emit('sayHello', 'Hello World from client');

    socket.on('halloToYouTo', data => {
        log(`event: helloToYouTo, ${data}`)
    });
});
Enter fullscreen mode Exit fullscreen mode

As you can see, this almost looks the same as the server. We have our .emit() and .on(). So when we are connected to the server, then we will .emit() the sayHello event and if we look back at service1.js then we will see that it is listening to that event and it will emit the helloToYouTo event. So we send a message to service1.js and got a message back. Easy, right?

Okay here is the full service2.js

const chalk = require('chalk')
const server = require('http').createServer();
const ioServer = require('socket.io')(server, {
    path: '/'
});
const ioClient = require('socket.io-client');

const log = (msg) => console.log(chalk.bold.cyan.bgBlack('Service2:'), chalk.white.bgBlack(`${msg}`))

// Server Code
ioServer.on('connection', client => {
    log('someone connected');
    client.on('sayHello', data => {
        log(`event: sayHello, data: ${data}`)
        client.emit('halloToYouTo', "hello from http://localhost:5000")
    });
    client.on('disconnect', () => {
        log('event: disconnect, client disconnected')
    });
});

// Client Code
const serverAddr = 'http://localhost:4000';
const socket = ioClient(serverAddr, {
    path: '/'
});
socket.on('connect', (data) => {
    log(`connected to ${serverAddr}`);

    socket.emit('sayHello', 'Hello World from client');

    socket.on('halloToYouTo', data => {
        log(`event: helloToYouTo, ${data}`);
    });
});

server.listen(5000);
log('started server on port 5000');
Enter fullscreen mode Exit fullscreen mode

Now we know how to connect two services!
In the repository, you will see three services!
Let us do something different! Instead of me explaining the implementation. I will show you the file and you down in the comments can try to explain how it works!
So here is the service3.js

const chalk = require('chalk')
const ioClient = require('socket.io-client');

const log = (msg) => console.log(chalk.bold.magenta.bgBlack('Service3:'), chalk.white.bgBlack(`${msg}`));

// Client Code
const clientAddr = 'http://localhost:5000';
const socket = ioClient(clientAddr, {
    path: '/'
});

socket.on('connect', (data) => {
    log(`connected to ${clientAddr}`);
    socket.emit('sayHello', 'Hello World from client');

    socket.on('halloToYouTo', data => {
        log(`event: halloToYouTo, data: ${data}`);
    });
});
log('started client ready');
Enter fullscreen mode Exit fullscreen mode

This is how your output should look:
Alt Text

And you can find the repo here

👋Say Hello! Instagram | Twitter | LinkedIn | Medium | Twitch | YouTube

Top comments (0)