UNPKG

h3

Version:

Minimal H(TTP) framework built for high performance and portability.

165 lines (124 loc) 4.89 kB
# 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); ```