Socket.io | Show Number of visitor
Tags: node, typescript, tutorial, webdev
Published: 2022-11-10 06:00:56

Hey 👋, I hope you're doing well,

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


![Long time](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1jj3h3s7wc8v5acf3nu.gif)

----

Today I want to talk to you a bit about [socket.io](https://socket.io)

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

Indeed I had to use it in recent days to display a counter of current visitors in real time on my personal site.

It was very simple, so we'll see how it goes!


----

Let's organize all this a bit with chapters :

## Table Of Contents
* [Creation](#chapter-1)
* [Code](#chapter-2)
* [Extra](#chapter-3)
* [Repo Github](#chapter-4)

----

## **Creation** <a name="chapter-1"></a>

So, if you allow me, let's start from the beginning, the creation of the project!

![Begun](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gb5leumnjcagaybcujls.gif)
We still have some expectations, in the sense that we are not going to "just" copy/paste code from the documentation.

We are going to make a small server with :

- [NodeJs](https://nodejs.org) (It's obvious)
- [Express](https://expressjs.com)
- [socket.io](https://socket.io)
- [Typescript](https://www.typescriptlang.org/) (My forever friend)
- [RxJS](https://rxjs.dev/) (Because it's great! And it will allow you to simply develop this project)

---

Come on, let's finally get started!

This part being quite simple, we will quickly go over the first commands.

### **We create a new folder for our project and enter on it** :

```bash
mkdir socketio-numberOfOnlineUser-ts
cd socketio-numberOfOnlineUser-ts
```
---

### **We init our new node project** :
```bash
npm init -y
```
(with -y cause we don't wanna loose time with the cli, -y approve all by default)

---

### **We add our beautiful dependencies !** :
```bash
npm i express rxjs socket.io
npm i -D @types/express @types/socket.io nodemon ts-node tslint typescript
```
---

### **Prepare the live reloading with this as script in your package.json**
```json
  "scripts": {
    "dev": "nodemon",
    "start": "ts-node src/index.ts",
  }
```
create a **nodemon.json** file bellow your package.json with :
```bash
touch nodemon.json
```

Like this ⬇️
![Nodemon config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pufmkqj6qs16tv17hnoh.png)

And put this on it (it will use this config for nodemon command, your *npm run dev*):
```json
{
  "ignore": ["**/*.test.ts", "**/*.spec.ts", "node_modules"],
  "watch": ["src"],
  "exec": "npm start",
  "ext": "ts"
}
```

---

###  **Init a tsconfig file :**
```bash
npx tsc --init --rootDir src --outDir dist --module commonjs --lib es2015  --sourceMap true --target es6 --esModuleInterop true
```

###  **Create our folders :**
```bash
mkdir src src/models src/services
```

*You will get this ⬇️*
![Project folders](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i612aubt7nilriurhfnf.png) 

###  **Create our files :**
```bash
touch src/index.ts src/index.html src/models/index.ts src/models/Socket.ts src/services/index.ts src/services/Socket.ts
```

*Normally at this stage your hierarchy should look like this ⬇️*
![Project folders and files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xel2yc7g9ve75fhvy9qn.png)
**For the creation stage of the project, we are good !**
**Next step !**

----

## **Code** <a name="chapter-2"></a>

**Finally some code ! The fun part !**

![Fun part](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p78hxgx5f2cen1nqroo8.gif)
So, let's open vscode and begin with the **index.ts** file.

For this part, we will init a basic node server under express, you know that, no seret here so it's what's it's look like :

```typescript
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?
});
```
---
At this stage we just have a nodejs server running and returning a blank page.

Let's go on.

---

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

Let's put this on it :

```html
<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("numberOfUser", function (data) {
  numberOfUsers.textContent = data;
});
</script>
```

Ah, finally our first reference to SocketIo, but what's going on?

Here we are initializing a SocketIo instance *(with io('backend adress'))* on the client side, this instance will try to connect to the socket "normally" available at your address localhost:3000, once connected this instance will wait for the emission of a "numberOfUser" event from your server *(with socket.on('') method)* .

However, your server doesn't have anything on this topic yet, so you'll just get an error if you test the page.

But let's continue.

---

We move on to the creation of the **data model**, because yes, we will send data from our socket, but to prevent the future complexity of our application we will stay clean and quickly create the data model.

Go on the **src/models** folder and put this into our file **Socket.ts**

```typescript
export interface NumberOfUser {
  data?: number;
}
```

Oh, but next to our Socket.ts file, we have an index.ts file, let's take care of it, in case it's a "barrel", useful to efficiently organize all our future import/export.

So, use it and put this into it :

```typescript
export * from "./Socket";
```

---

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

Now let's take care of our Socket instance ! Finally !

So let's take care of the **src/services** folder, inside we have our little barrel **index.ts** (now you know what to do with it right? export blablabla)

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

We will finally use RxJS to create a variable that will be "Observable".

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

So we can make our socket react easily and dynamically!

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

In short here is the state of our famous file ⬇️

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

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

export class SocketService {
  private numberOfUser: BehaviorSubject<NumberOFUser> =
    new BehaviorSubject<NumberOFUser>({
      data: 0,
    }); // Here our "stream pipe" of the NumberOfUser Data.
  private numberOfUser$: Observable<NumberOFUser> =
    this.numberOfUser.asObservable(); // 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.numberOfUser$.subscribe((store) => {
      // subscribe is called at each store value change !
      this.io.emit("numberOfUser", store.data); // So at each value change, we emit this value to the client with the event name "numberOfUser"
    });
  }

  public listenUserActivity() {
    this.io.on("connection", (client) => {
      // When a user do a connection at our socket, the "connection" event is called. It's a default event from socket.io.
      this.numberOfUser.next({
        data: this.numberOfUser.value.data + 1, // we get the actual value of our "Observable" and
      }); // we change the data with the function "next"

      client.once("disconnect", () => {
        // When a user do a disconnection, the "disconnect" event is called. It's a default event from socket.io.
        this.numberOfUser.next({
          data: this.numberOfUser.value.data - 1,
        }); // You know what next do now.
      });
    });
  }
}

```

And that it for the socket.ts file !

----

Now that we have a ready-made Socket service with the logic we want, we need to let our server know it exists.

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

```typescript
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 "numberOfUser" 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}`);
});

```

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

![Socket result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcdrefbhlxgbvbpx402i.gif)

When we load the page, we wan see the **numberOfUser** value change, also when we left the page.

It's work !

![Mission accomplished](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mscxfcy5km2ccn0w7np.gif)

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

## **Extra** <a name="chapter-3"></a>

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

No problem, just use the [socket.io-client](https://www.npmjs.com/package/socket.io-client) package on the front side and do like this ⬇️: 

```typescript
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("numberOfUser", (data) => {
      this.emit(data); // At each "numberOfUser" 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 !
  }
}
```

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

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


![Ok, Goodbye !](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mote2nql7z260jlpgxoa.gif)

<center>See you !</center>


## **Repo Github** <a name="chapter-4"></a>
{% github NOPR9D/socketio-numberOfOnlineUser-ts %}

*[My portfolio in development where I implemented this socket](https://www.mylabz.xyz)*

*If you have any ideas, I'm listening! I hesitate to allow users to send each other emoji for example*

*edit 11/14/2022 : Set things a little more generic / Or simpler to add other "Observables" value*