# WebSockets

> H3 has built-in utilities for cross platform WebSocket and Server-Sent Events.

You can add cross platform WebSocket support to H3 servers using [🔌 CrossWS](https://crossws.h3.dev/).

## Usage

WebSocket handlers can be defined using the `defineWebSocketHandler()` utility and registered to any route like event handlers.

You need to register CrossWS as a server plugin in the `serve` function. The plugin resolves the correct hooks from your matched route automatically.

```js
import { H3, serve, defineWebSocketHandler } from "h3";

import { plugin as ws } from "crossws/server";

const app = new H3();

app.get("/_ws", defineWebSocketHandler({ message: console.log }));

serve(app, {
  plugins: [ws()],
});
```

> [!NOTE]
> Passing a custom `resolve` to `ws()` is only needed to resolve hooks yourself (for example without invoking the app). By default, CrossWS calls the app's `fetch` handler and reads the hooks attached by `defineWebSocketHandler()`.

**Full example:**

```js [websocket.mjs]
import { H3, serve, html, defineWebSocketHandler } from "h3";
import { plugin as ws } from "crossws/server";

export const app = new H3();

// A minimal self-contained WebSocket playground served for plain HTTP requests.
const playground = html`<!doctype html>
  <title>H3 WebSocket Playground</title>
  <h1>H3 WebSocket Playground</h1>
  <form id="form">
    <input id="input" placeholder="Type a message..." autocomplete="off" autofocus />
    <button type="submit">Send</button>
  </form>
  <div id="log"></div>
  <script type="module">
    const log = (msg) => {
      const line = document.createElement("div");
      line.textContent = msg;
      document.getElementById("log").append(line);
    };
    const url = location.href.replace(/^http/, "ws");
    const ws = new WebSocket(url);
    ws.addEventListener("open", () => log("[open] connected to " + url));
    ws.addEventListener("message", (e) => log("[message] " + e.data));
    ws.addEventListener("close", () => log("[close] disconnected"));
    document.getElementById("form").addEventListener("submit", (e) => {
      e.preventDefault();
      const input = document.getElementById("input");
      ws.send(input.value);
      input.value = "";
    });
  </script>`;

// A single route serves both the playground page (plain HTTP) and the
// WebSocket endpoint (upgrade requests). The page connects back to itself.
app.get(
  "/",
  defineWebSocketHandler(
    {
      open(peer) {
        console.log("[open]", peer);

        // Send welcome to the new client
        peer.send("Welcome to the server!");

        // Join new client to the "chat" channel
        peer.subscribe("chat");

        // Notify every other connected client
        peer.publish("chat", `[system] ${peer} joined!`);
      },

      message(peer, message) {
        console.log("[message]", peer);

        if (message.text() === "ping") {
          // Reply to the client with a ping response
          peer.send("pong");
          return;
        }

        // The server re-broadcasts incoming messages to everyone
        peer.publish("chat", `[${peer}] ${message}`);

        // Echo the message back to the sender
        peer.send(message);
      },

      close(peer) {
        console.log("[close]", peer);
        peer.publish("chat", `[system] ${peer} has left the chat!`);
        peer.unsubscribe("chat");
      },
    },
    // Non-upgrade requests get the playground page.
    () => playground,
  ),
);

serve(app, {
  plugins: [ws()],
});
```

### Handling HTTP requests

By default, a WebSocket route responds with `426 Upgrade Required` to any request that is not a WebSocket upgrade.

You can pass an optional HTTP handler as the second argument to `defineWebSocketHandler()` to serve regular (non-upgrade) requests on the same route. WebSocket upgrade requests still go to the hooks.

```js
app.get(
  "/_ws",
  defineWebSocketHandler(
    { message: (peer, message) => peer.send(message.text()) },
    () => "Send a WebSocket upgrade request to connect.",
  ),
);
```

## Server-Sent Events (SSE)

As an alternative to WebSockets, you can use [Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).

H3 has a built-in API to create server-sent events using `createEventStream(event)` utility.

### Example

```js [server-sent-events.mjs]
import { H3, serve, createEventStream } from "h3";

export const app = new H3();

app.get("/", (event) => {
  const eventStream = createEventStream(event);

  // Send a message every second
  const interval = setInterval(async () => {
    await eventStream.push("Hello world");
  }, 1000);

  // cleanup the interval when the connection is terminated or the writer is closed
  eventStream.onClosed(() => {
    console.log("Connection closed");
    clearInterval(interval);
  });

  return eventStream.send();
});

serve(app);
```
