Websockets

WebSockets are a technology that provides full-duplex, persistent communication channels between a web server and a web client (typically a web browser).

Unlike HTTP, which is request-response based, WebSockets allow for continuous, bi-directional data exchange. This makes them ideal for applications that require real-time updates, such as online chat, collaborative editing, and real-time data visualization.

A Chat Application

Deno have websocket server builtin. Let's use Deno to write a simple chat application.

Add a server.js:

const clients = []

Deno.serve({
  port: 3000,
  handler: async (request) => {
    if (request.headers.get("upgrade") == "websocket") {
      const { socket, response } = Deno.upgradeWebSocket(request)
      
      socket.onmessage = event => {
        clients.forEach(x => {
          if (x != socket) {
            x.send(event.data)
          }
        })
      }

      socket.onclose = () => clients.splice(clients.indexOf(socket), 1)
      socket.onerror = error => console.error("ERROR:", error)

      socket.onopen = () => clients.push(socket)
      return response
    }
    const file = await Deno.open("./index.html", { read: true })
    return new Response(file.readable)
  },
})

Add a index.html:

<!doctype html>
<style>
    #container {
      width: 400px;
      padding: 50px;
      display: flex;
      gap: 20px;
      flex-direction: column;
    }
    #output {
      height: 300px;
      border: 1px solid #ccc;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      gap: 5px;
      padding: 5px;
    }
    .text {
      background: green;
      color: white;
      border-radius: 5px;
      padding: 5px;
    }
    .self {align-self: flex-end;}
    .error {background: red;}
</style>
<div id="container">
  <div id="output"></div>
  <textarea id="input" rows=3></textarea>
</div>
<script>
  const ws = new WebSocket("ws://127.0.0.1:3000")
  const input = document.querySelector("#input")

  function appendMessage(msg, type) {
    const template = document.createElement("template")
    template.innerHTML = `<div class="text ${type}">${msg}</div>`
    document.querySelector("#output").appendChild(template.content.cloneNode(true))
  }

  input.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        event.preventDefault()
        ws.send(input.value)
        appendMessage(input.value, 'self')
        input.value = ""
    }
  })

  ws.onmessage = (e) => appendMessage(e.data, 'incoming')
  ws.onerror = (e) => appendMessage(e.data, 'error')
</script>

Start the server, and open two browser tabs to chat.

deno run --allow-net=0.0.0.0:3000 --allow-read=index.html server.js