DEV Community

Ian Wilson
Ian Wilson

Posted on

Build Real-time Apps by Learning WebSockets

As a studying web developer, once you learn to query external APIs to display data in your UI, a whole new world opens up.

When interviewing for UI developer positions at various companies, I would say something like, "just give me the endpoints and I'll handle the rest ;)".

All of their other questions like "How do you handle adversity?" or "How do you handle disputes with stakeholders?" become moot.

With HTTP requests, I can get data, I can post data, and I can delete data. I do it all - I am definitely a fit for your team whether you know it or not.

This sounds great in the echo chamber of my mind. I distinctly remember feeling like a champion developer because I knew how to send requests to REST APIs.

Then, they had me make a chat application.

The Issue with Polling

If you're like me, you didn't understand how chat apps worked when you became a web developer. You might've thought, hey I'll just send a GET request every half-second or so to retrieve the recent chat messages.

In JavaScript, that might look a little something like this:

let clearIntervalId
let messages = []

function pollChatMessages() {
  const clearIntervalId = setInterval(() => {
    messages = await axios.get('https://api.example.com/threads/1/messages') // totally fake url
  }, 500)
}
Enter fullscreen mode Exit fullscreen mode

This is called polling, and it'll work for a little while. One of my clients couldn't tell the difference, so long as there were only a couple users.

This function would be executed on some interval inside a singleton that exists inside the scope of our web application. If we wanted to kill the polling we could call clearInterval(clearIntervalId).

If we were chatting with 10 people, we would be running 10 polls from our browser. Likewise, those 10 people would also be running a poll for each person they were chatting with.

And what if some threads have hundreds of messages? That is a ton of needlessly large requests for a simple chat app.

The problem here is that the use of polling assumes that there is no way for a client like our browser to subscribe to a server. We can do better with a little networking.

Networking Basics

Let's start with some networking basics, what is a socket?

A TCP socket is an endpoint instance defined by the combination of an IP address with a port, in the context of either a listening state (a server) or a particular TCP connection (a client, like your browser).

A TCP connection is defined by the pairing of two sockets.

There are three main kinds of transports that we commonly use in browser web applications:

  • XMLHTTPRequests, or just HTTP for short. Send a single request and get a single response. These are pretty common.
  • Server-Sent Events, or SSE. Send a long-lived request and be able to stream data from the server. Great for real-time data streaming, particularly when the client doesn't need to send messages back to the server.
  • WebSockets, the only transport that allows for bidirectional streaming of text and binary data. We'll dive a little further into it.

Here's a diagram I stole from High-Performance Browser Networking, which illustrates the communication flow between each of these transports. It's a good book to check out if you're serious about improving the performance of your web apps.

ws

In most tutorials that deal with external data, you'll deal with the left-most paradigm's HTTP requests. Actually, all of these flows are initiated with an HTTP request, as shown by the blue arrows.

I don't commonly see articles or tutorials on SSE, but MDN has a good reference in case a one-way data stream sounds intriguing.

The third flow is most interesting to us - it gives us the ability to communicate with the server over a single long-lived connection.

Enter WebSockets

As described by the Mozilla Developer Docs,

WebSockets are an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

That's fantastic, we don't need to do any polling! But how does it work?

The lifecycle of a WebSocket connection between a client and server goes like this. Imagine our server is hosted at https://api.example.com on port 8080 and our client is someone's browser.

  1. The client sends a GET request to api.example.com:8080, with a couple of headers that indicate it wants to establish a WebSocket connection with the server. One of these is called Sec-WebSocket-Key, and is used to secure the connection between the client and server. The server derives a response header from this key called Sec-WebSocket-Accept, which indicates that the server does indeed support WebSockets and that it doesn't try to process the request as an ordinary HTTP request.
  2. The server responds with code 101 - Switching Protocols, indicating that the handshake is complete and the client/server can start exchanging messages encrypted over XOR. Sparing the details of this data masking, we can now publish and subscribe to text or binary messages over this connection.
  3. At any point after a successful handshake either the client or server can send a ping in order to check whether the other party is still connected. If the recipient of the ping does not send back a pong, it may have been disconnected.

In JavaScript, we can connect to a WebSocket server like this:

const thread = document.getElementById('chat-thread-1')

const conn = new WebSocket('ws://api.example.com/threads/1')

conn.onclose = function(event) {
  console.log('Connection closed')
}

conn.onmessage = function(event) {
  console.log('Message received.')
  const message = document.createElement('p')
  message.textContent = event.data
  thread.append(message)
}
Enter fullscreen mode Exit fullscreen mode

We can invoke the built-in WebSocket constructor to create a connection, at which point we can set up the event handlers to decides what happens when a message is received.

We can also send messages, what if we had an input element that the client could enter text to chat with other users? That sure would be useful.

function sendMessage() {
  const input = document.getElementById('chat-thread-1-input')
  conn.send(input.value)
  input.value = ''
}
Enter fullscreen mode Exit fullscreen mode

Better yet, what if we wanted to communicate more complex data with a message type and a timestamp, perhaps in the form of JSON?

function sendMessage() {
  const input = document.getElementById('chat-thread-1-input')

  const message = {
    type: 'message',
    text: input.value,
    date: Date.now(),
  }

  conn.send(JSON.stringify(message))
  input.value = ''
}
Enter fullscreen mode Exit fullscreen mode

With a little DOM manipulation, we are not far from a somewhat real-world chat application. Chats are neat and all, but what else can we do with this technology?

Other Applications of WebSockets

For this chat example, the application of WebSockets is obvious: send and receive text messages.

If you frequent dev.to then you might've noticed that they have a contest going on, called Build a Realtime App with Pusher.

The Pusher API is built on the basis of WebSockets. Some of its use cases include:

  • updating location in real-time, say for journies and deliveries
  • real-time graphs
  • collaboration over a browser, mobile, or IoT
  • online gaming

Personally, I'm thinking of building a cloud markdown editor that'll help me with editing and sharing posts in a single place. I'd store my markdown content on a server and ideally be able to view/edit it from my Mac or my phone.

If I want to share a post with someone to edit, I'd drop them a link and I'd be able to see their cursor and notes in real-time, similar to how a Google doc works.

Of course, part of the fun for me is implementing the server myself. Whether I'd consume a service like Pusher will depend on my productivity/pain with the implementation.

If this intrigues you as well, you may find these links useful:

Curious for more posts or witty remarks? Follow me on Medium, Github and Twitter!

Discussion (1)

Collapse
rajeshbabu1991 profile image
Rajesh Babu

Keep writing such amazing stuff :)