Primate
Guides Responses

Implement a real-time chat with WebSocket

You can use the ws handler to upgrade a GET route to WebSocket. To use this to implement a real-time chat application, first create a HTML client.

This assumes you have installed and loaded @primate/html in your config file.

The HTML frontend will automatically extract <style> and <script> tags and bundle them into the rest of your application.

HTMLcomponents/index.html
<script>
  window.addEventListener("load", () => {
    const ws = new WebSocket(new URL("/ws?limit=20", window.location.href));
    ws.addEventListener("open", () => {
      document.querySelector("#chat").addEventListener("keypress", event => {
        if (event.key === "Enter") {
          ws.send(event.target.value);
          event.target.value = "";
        }
      })
    });
    ws.addEventListener("message", message => {
      const chatbox = document.querySelector("#box");
      const div = document.createElement("div");
      div.innerText = message.data;
      chatbox.appendChild(div);
    })
  });
</script>
<style>
  #box {
    background-color: lightgrey;
    width: 300px;
    height: 300px;
    overflow: auto;
  }
</style>
<div id="box"></div>
<input id="chat" placeholder="Type to chat" />

Serve the client from the index route (/).

TypeScriptroutes/index.ts
import response from "primate/response";
import route from "primate/route";

route.get(() => response.view("index.html"));

At the /ws route, upgrade with the ws handler, maintaining a list of active web socket connections and distributing messages to all active chat participants.

TypeScriptroutes/ws.ts
import uint from "pema/uint";
import response from "primate/response";
import route from "primate/route";

const sockets = new Set<any>();

route.get(request => {
  // limit the number of messages a client can send
  const limit = uint.coerce.default(20).parse(request.query.get("limit"));
  let n = 1;
  return response.ws({
    close(socket) {
      sockets.delete(socket);
    },
    message(_, message) {
      [...sockets.values()].forEach(socket => {
        if (n > 0 && n < limit) {
          n++;
          socket.send(message);
        }
      });
    },
    open(socket) {
      sockets.add(socket);
    },
  });
});