As part of my freelancing journey, I have recently being asked to perform a technical test. The content of that test was as follow:
- Users must be notified when another user connects or disconnects
- The application will send an offer every 15 seconds. This offer will have a random price and can be accepted by the users. After, 30 seconds, that offer expires.
- Users must be notified when an other user accepts an offer.
- Users will be authenticated by their IP addresses.
- Users can see the offers they accepted in the past and the other users connected.
- Everything must be in real-time.
Ok, so this was the test. Build a web application that follows those stories. The real-time part is obviously crucial here, and it makes perfect sense. As a potential buyer, I can only buy offers that have not been accepted by another buyer. So, the updates on the offers must be done in real-time. Same thing concerning the connected users. Everybody will have the same information at the same time. One user can’t be shown connected for one user, and disconnected for some.
Now, the previous accepted offers was an interested challenge. Indeed, this feature will change for every single user. Everybody will have accepted different offers. One offer can only be accepted by one user at a time. This part of the application was the most difficult to solve for me. Let me give you a quick overlook of how I did it.
First, what technologies should I use? The real-time concept made me choose socket.io. I honestly haven’t looked around for other libraries that would allow you to use WebSocket in this way. WebSocket is a web protocol that allows the client and the server to communicate to one another (in real-time). You can find more information about socket.io here.
At the time, my knowledge about this library was kind of limited. I knew the basics, but the idea of sockets sending data to only certain sockets was new to me.
If you use the socket.emit() method, the socket will send whatever data it has in its argument to ALL the other sockets, including itself. That is the perfect function for a chat room for example. If a send a message, I want everyone to see it, including myself. Great, but in this case, I’m only looking for that when I display the offers. The server sends the available offers for everybody. What about the other use cases?
Let’s talk about the notifications first. The connection, disconnection of a user, and the user accepting an offer. Here, I want to send the data to everybody except the user that is responsible for that action. In socket.io, it means the socket responsible broadcasts the data to the other sockets. Like this:
socket.broadcast.emit('notification', 'User just connected');
Done! I just joined the party, but only the other participants have been notified. Problem solved.
Next, the accepted offers issue. This one was tougher. I couldn’t get my head around this. How do you send the concerned data to the only socket that needs it? I tried to use the socket.broadcast.to(socketID) method. But that didn’t work. I think this method is only reserved to send data to other sockets. And in this case, I was retrieving the accepted offers from a database, and sending that to the socket. the socket was sending the data to ITSELF. And then, after much research, I found a way to do it.
That’s right. You see, in socket.io, you can group sockets in rooms. Think about chat rooms, it works the same! When I join a room in a chat application, I won’t receive the messages from other rooms I’m not a part of. Well, that’s exactly what I needed to do here! Create a room for each user, and broadcast the data of their offers to their room of one. I needed to make sure that each room would have a unique name, so different users would not have colliding room names (therefore wrong data concerning offers) by accident. Well, remember the list of user stories. Users are recognized by IP addresses, so:
//get IP address of the socket at the connection const userIP = socket.handshake.address; //create unique room socket.join(userIP);
Then, in order to send data to this particular socket, I used:
And voila! Use the io global to get all sockets and filter the existing rooms. I tell the application to only send the data to the sockets in that particular room. And because I used the IP addresses, there is only one member in each room, and no risk of sending data to the wrong person.
If you want to see the application live, it is hosted on Heroku here.
As usual, I’m sure there is a better way to do this, more elegant way. But, the first step is to make it work. It does! I would probably need more intel about the WebSocket protocol and the socket.io library. But it was a really fun little project to work on.
As always, thank you for your time, and feel free to share and comment!
Have a nice day!