How I brought Yahoo Chat back to life in 5 hours: Part 2 — The WebSocket

Hope you didn’t forget about this grainy reference photo!

I was originally going to recap part one of my journey resurrecting Yahoo Chat, but you should read it here yourself because it’s actually really cool and, if you’re asking me, I think it’s not half-bad. But… if you don’t feel like it, I’m basically just reconstructing the most crude version of Yahoo Chat I could probably create. I may not officially be a UI engineer, but my inner UI designer is definitely screaming right now.

What is a WebSocket?

Ah, the question that has been haunting me for longer than I’d like to admit. What is a WebSocket and why are we going to be using it? The following definition is from wikipedia.

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.

Basically, it allows a browser to connect to a WebSocket server to transfer data back and forth. If you’re still not getting it, think of messages sending and receiving in a chat room. HEY, wait, that’s exactly what we’re going to be doing with it!

WebSocket Server Setup

We are actually going to be making a new directory for our backend in our current project’s root directory. Once it’s made, we will need to install a WebSocket client called ws (docs) and create a server.js file.

mkdir chatroom-backend
cd chatroom-backend
yarn add ws
touch server.js

The server.js file is what our WebSocket is going to run on in a backend server-type situation. Since we already added ws to our backend, we’re going to create a WebSocket object and then create a server object on port 3030.

After that, we’re going to check if the socket is on, if there’s a connection, and then broadcast all messages to each connected WebSocket clients, including itself, using the 12 lines of code below. This is the entire backend portion of the WebSocket set up! This, admittedly, is what I struggled with the most.

//server.jsconst WebSocket = require('ws');
const socket = new WebSocket.Server({ port: 3030 });
socket.on('connection', ws => {
ws.on('message', data => {
socket.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(data)
}
})
})
})

Adding The Server Event Listeners

We have our WebSocket set up, but the frontend doesn’t know about it yet. I went to my TextBox component and defined socket as the url we defined before in the backend so that we can access it and add onOpen, onMessage, and onClose listeners.

//TextBox.jssocket = new WebSocket('ws://localhost:3030')
Component Layout (just in case you forgot!)

Now, I added all of this into the TextBox component because that was the thing that was going to be sending the messages. I definitely could have thought this out better and maybe passed it down through props, but my 5 hour deadline was coming to a close and I was ready to get this project checked off my list!

I added the following code to my form’s handleSubmit function. It takes the message that the user typed and sends it to the server. The code in server.js takes it and broadcasts it to every single user, therefore sending the message to all the connected clients! YAY!

//TextBox.js//send message to the WebSocket
this.socket.send(JSON.stringify(messageObject))

this.socket.onopen broadcasts the username to all the connected clients. I ran into a bug where already-connected users don’t populate on new user’s “Users:” lists, but would like to revisit this project as a whole and see if I could add a way to store connected users on the WebSocket itself! I’m sure there is, but I couldn’t find any documentation on it.

this.socket.onmessage takes the message and dispatches it to state. I’m passing this.props.users because I created a workaround for the bug I mentioned before that automatically adds users to your user list after they send a message if they weren’t on there before!

this.socket.onclose attempts to reconnect to the server if your connection is somehow disconnected on accident, but then removes you from the list of users if it doesn’t succeed.

//TextBox.jscomponentDidMount() {
this.socket.onopen = () => {
//sends username to server for welcome message
this.socket.send(JSON.stringify(this.props.username))
}
this.socket.onmessage = (e) => {
const message = JSON.parse(e.data)
//this came from dispatchStateToProps
this.props.handleNewMessage(message, this.props.messages, this.props.users)
}
this.socket.onclose = () => {
//attempts to reconnect to server if disconnected on error
this.setState({
socket: new WebSocket('ws://localhost:3030'),
})
this.props.handleClose(this.props.users, this.props.username)
}
}

The Final Result

From NewUser377’s POV
From NewUser839’s POV

Somehow I got it all to work, and gosh darn it did it feel good. Is it perfect? No. Would I like to come back to this project and make it a thousand times better? Yes! Did I conquer my arch nemesis, the WebSocket, and learn how to use them? You bet your cute behind I did!!!

How to Start Up Your WebSocket

There’s only two easy steps to getting everything online!

cd chatroom-frontend
yarn start

and

cd chatroom-backend
node server.js

If you want to see and follow along with my code, you can find it here. I’m also on LinkedIn and Twitter if you’d like to connect! Now go out with your new knowledge and try make something great today in only 5 hours. Tag me on twitter if you succeed!

Full-Stack Engineer, UX/UI Aficionado, and lover of coffee.