Socket.io | show visitor count

hey i hope you are doing well,

It’s been a long time since we’ve seen each other!


Today I want to talk to you about sockets

It is very nice little library that allows us to easily manage our sockets under nodejs.

In fact I’ve had to use it in the recent past to display a counter of current visitors to my personal site in real time.

It was super easy, so we’ll see how it goes!


Let’s organize all this a little with chapters:

Table of Contents


Nature

So, if you allow me, let’s start from the beginning, building the project!

Started
We still have some expectations, in the sense that we’re not going to “just” copy/paste the code from the documentation.

We are going to build a small server with:


Let’s finally begin!

Since this part is fairly simple, we’ll quickly go over the first commands.

We create a new folder for our project and enter ,

mkdir socketio-numberOfOnlineUser-ts
cd socketio-numberOfOnlineUser-ts
enter fullscreen mode

exit fullscreen mode


We start our new node project ,

npm init -y
enter fullscreen mode

exit fullscreen mode

(with -y reason we don’t want to waste time with cli, accept -y all by default)


We add our beautiful dependencies! ,

npm i express rxjs socket.io
npm i -D @types/express @types/socket.io nodemon ts-node tslint typescript
enter fullscreen mode

exit fullscreen mode


Prepare live reloading with it as script in your package.json

  "scripts": {
    "dev": "nodemon",
    "start": "ts-node src/index.ts",
  }
enter fullscreen mode

exit fullscreen mode

make nodemon.json File your package.json with:

touch nodemon.json
enter fullscreen mode

exit fullscreen mode

like this
nodemon config

and put this on (it will use this config for nodemon commands, your npm run dev,

{
  "ignore": ["**/*.test.ts", "**/*.spec.ts", "node_modules"],
  "watch": ["src"],
  "exec": "npm start",
  "ext": "ts"
}
enter fullscreen mode

exit fullscreen mode


Init a tsconfig file:

npx tsc --init --rootDir src --outDir dist --module commonjs --lib es2015  --sourceMap true --target es6 --esModuleInterop true
enter fullscreen mode

exit fullscreen mode

Create our folders:

mkdir src src/models src/services
enter fullscreen mode

exit fullscreen mode

you will get it
project folder

Create our files:

touch src/index.ts src/index.html src/models/index.ts src/models/Socket.ts src/services/index.ts src/services/Socket.ts
enter fullscreen mode

exit fullscreen mode

Normally your hierarchy at this level should look like this ️
Project folders and files
For the construction phase of the project, we are good!
Next Step !


code

Finally some code! fun part!

the fun part
So, let’s open vscode and start with index.ts file.

For this part, we’ll go into a basic node server under express, you know, there’s no cert here, so here’s what it looks like:

import express from "express"; // We import express as listener, you know.
import * as http from "http"; // We import http for init the server
// Anyway, if we don't do it, express does it secretly

const app =  express(); // Init express
const server = http.createServer(app); // Init server avec use express as listener
const port =  3000;

app.get("/", (req, res) => {
res.sendFile(__dirname +  "/index.html"); // We Will use the index.html file to see our work
});

server.listen(port, () => {
return console.log(`server is listening on ${port}`); // No need to comment that, no?
});
enter fullscreen mode

exit fullscreen mode


At this stage we have just one nodejs server running and returning a blank page.

Let’s go ahead.


Let’s prepare our index.html file, we just want to see the number of users online in real time.

Let’s put it at this:

<div>
    <p>Number of users : <span id="numberOfUsers"></span></p>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>

var socket = io("localhost:3000");
var numberOfUsers = document.getElementById("numberOfUsers");

socket.on("dataChange", function (data) {
  numberOfUsers.textContent = data.numberOfUser;
});
</script>
enter fullscreen mode

exit fullscreen mode

Ah, finally our first reference to SocketIO, but what’s up?

Here we are initializing a SocketIo instance (with io(‘BackendAddress’)) On the client side, this instance will try to connect to a socket “normally” available at your address localhost:3000, once connected this instance will wait for the emission of a “data changed” event from your server (with socket.on(”) method) ,

However, your server doesn’t have anything on the subject yet, so you’ll only get an error if you test the page.

But let’s continue.


We proceed to build data modelBecause yes, we will be sending data from our socket, but to prevent future complexity of our application we will be clean and build the data model quickly.

go on source/model folder and put it in our file socket.ts

export interface SocketData {
  numberOfUser?: number;
}
enter fullscreen mode

exit fullscreen mode

Oh, but next to our Socket.ts file, we have an index.ts file, let’s keep in mind that, if it’s a “barrel”, useful to efficiently organize all our future imports/exports.

So, use it and put it in:

export * from "./Socket";
enter fullscreen mode

exit fullscreen mode


Ok, our model is ok, now we know what the data passing through our socket looks like.

Now take care of our socket instance! eventually !

so let’s take care of it source/services folder, we have our little barrel inside index.ts (now you know what to do with it? export blablabla)

Let’s move on to the most important file, src/services/Socket.ts

We will eventually use RxJS to create a variable that will be “observable”.

That is to say, a function (a callback) is to be executed on each change of the data contained in this variable.

So we can make our sockets respond easily and dynamically!

We will also have a function that increments or decrements numberOfUser So that the client can see the changes when a visitor logs in/logs out.

In short here is the status of our famous file ️

import * as socketio from "socket.io";
import * as http from "http"; // Just for typing, we like typing !
import { BehaviorSubject } from "rxjs";

import { SocketData } from "../models/Socket"; // Here our model, cause we like typing things !

export class SocketService {
  private store: BehaviorSubject<SocketData> = new BehaviorSubject<SocketData>({
    numberOfUser: 0,
  }); // Here's our "Observable" data.
  private io!: socketio.Server; // Our Socket instance

  constructor(server: http.Server) {
    this.io = new socketio.Server(server, { cors: { origin: "*" } }); // We init our Socket instance with the server as parameter
  }

  public listenStore() {
    this.store.subscribe((store) => {
      // subscribe is called at each store value change !
      this.io.emit("dataChange", store); // So at each value change, we emit this value to the client with the event name "dataChange"
    });
  }

  public listenUserActivity() {
    this.io.on("connection", (client) => {
      const storeData = this.store.getValue(); // we get the actual value of our "Observable"
      // When a user do a connection at our socket, the "connection" event is called. It's a default event from socket.io.
      this.store.next({ numberOfUser: storeData.numberOfUser + 1 }); // so we change the data of the store with the function "next"
      client.once("disconnect", () => {
        const storeData = this.store.getValue(); // we get the actual value of our "Observable"
        // When a user do a disconnection, the "disconnect" event is called. It's a default event from socket.io.
        if (storeData.numberOfUser !== 0) {
          // A check just by security.
          this.store.next({ numberOfUser: storeData.numberOfUser - 1 }); // You know what next do now.
        }
      });
    });
  }
}

enter fullscreen mode

exit fullscreen mode

And that for the socket.ts file!


Now that we have a ready socket service with the logic we want, we need to tell our server that it exists.

To do this we will edit the src/index.ts file like this

import express from "express";
import * as http from "http";
import { SocketService } from "./services"; // We import our SocketService, Thanks to the index.ts file, we don't need to specify where exactly the Socket.ts file is located.

const app = express();
const server = http.createServer(app);
const port = 3000;

const socketService = new SocketService(server); // We instantiate our SocketService

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

socketService.listenStore(); // We ask to our service to listen the change of the "Observable" data and emit the change to our socket by the "dataChange" event.

socketService.listenUserActivity(); // We listen the activity of the socket and change the state of the "Observable" data when a desired event is triggered ("connection" or "disconnect")

server.listen(port, () => {
  return console.log(`server is listening on ${port}`);
});

enter fullscreen mode

exit fullscreen mode

So if we come back to the client side, we can see all our work!

socket result

When we load the page, we want to see numberOfUser Price changes, that too when we left the page.

That’s the job!

mission accomplished

So you can start from there to make your logic more complex!

Excessive

But what are you saying? Your client is not on the node side? You are using front framework like Vuejs/Angular or Next.

Never mind, just use socket.io-client package on front end and do like this ️:

import { io, Socket } from "socket.io-client";
import { Observable, BehaviorSubject } from "rxjs";

export interface SocketData {
  numberOfUser?: number;
}

export class SocketService {
  private socket!: Socket;

  private store: BehaviorSubject<SocketData> = new BehaviorSubject<SocketData>({
    numberOfUser: 0,
  });
  private store$: Observable<SocketData> = this.store.asObservable();

  constructor() {
    this.socket = io("YOUR_BACKEND_URL");
    this.socket.on("dataChange", (data) => {
      this.emit(data); // At each "dataChange" event we modify our "Observable" data.
    });
  }

  public emit(store: SocketData): void {
    this.store.next(store);
  }

  public listen(): Observable<SocketData> {
    return this.store$; // You will be able to use this.socketService.listen().subscribe(), it's the same logic as we see above !
  }
}
enter fullscreen mode

exit fullscreen mode

I think we’ve seen the main part, so we can stop here!

In the next post, we will see how to deploy it under apache2 under your own VPS, you will see that it is simple!

ok bye bye!

see you !

repo github

Code of Devto article “Show number of socketIO visitors”

Article Code Socket.io | show visitor count

How to do

establish

npm i
enter fullscreen mode

exit fullscreen mode

run

npm start dev
enter fullscreen mode

exit fullscreen mode

Leave a Comment