Guide

WebSockets

H3 has built-in support for cross platform WebSocket and SSE.


H3 natively supports runtime agnostic WebSocket API using CrossWS.

You can define WebSocket handlers in your existing Event Handlers to define multiple websocket handlers, dynamically matched with your same route definitions!
Read more in WebSocket in MDN.
Read more in CrossWS.
WebSockets support is currently experimental and available in nightly channel.

Usage

Example

You can use npx listhen --ws -w websocket.ts to run this example
websocket.ts
import { createApp, defineEventHandler, defineWebSocketHandler } from "h3";

export const app = createApp();

app.use(
  defineEventHandler(() =>
    fetch(
      "https://raw.githubusercontent.com/unjs/crossws/main/examples/h3/public/index.html",
    ).then((r) => r.text()),
  ),
);

app.use(
  "/_ws",
  defineWebSocketHandler({
    open(peer) {
      console.log("[ws] open", peer);
    },

    message(peer, message) {
      console.log("[ws] message", peer, message);
      if (message.text().includes("ping")) {
        peer.send("pong");
      }
    },

    close(peer, event) {
      console.log("[ws] close", peer, event);
    },

    error(peer, error) {
      console.log("[ws] error", peer, error);
    },
  }),
);

Server Sent Events (SSE)

As an alternative to WebSockets, you can use Server-sent events.

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

SSE support is currently experimental and available in nightly channel.

Example

server-sent-events.ts
import { createApp, createRouter, eventHandler, createEventStream } from "h3";

export const app = createApp();

const router = createRouter();
app.use(router);

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

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

    // cleanup the interval and close the stream when the connection is terminated
    eventStream.onClosed(async () => {
      clearInterval(interval);
      await eventStream.close();
    });

    return eventStream.send();
  }),
);