Primate Logo 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.

HTMLviews/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";

export default route({
  get() {
    return 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 p from "pema";
import response from "primate/response";
import route from "primate/route";

const sockets = new Set<any>();

export default route({
  get(request) {
    // limit the number of messages a client can send
    const limit = p.loose.uint.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);
      },
    });
  },
});