Quickstart
There is nothing to install and nothing to create. Pick a channel name, connect, and you are done.
1. Choose a channel name
The name is the address and the only access control, so make it unguessable. Six characters is the enforced minimum; a random 32 hex characters is a sensible default.
const channel = crypto.randomUUID();2. Connect
const ws = new WebSocket(
`wss://router.metapage.io/${channel}`,
);
ws.addEventListener("message", (event) => {
console.log("from a peer:", event.data);
});
ws.addEventListener("open", () => {
ws.send("hello everyone else on this channel");
});Open that same snippet in a second tab. Each tab sees the other's message, and neither sees its own.
Use wss:// in production
ws:// is fine against localhost, but browsers refuse insecure websockets from an https:// page.
3. Send from anywhere else
Any HTTP client can broadcast into a channel. The request body is delivered verbatim to every listener.
curl -X POST \
https://router.metapage.io/CHANNEL \
-d '{"temperature": 21.4}'await fetch(`https://router.metapage.io/${channel}`, {
method: "POST",
body: JSON.stringify({ temperature: 21.4 }),
});import requests
requests.post(
f"https://router.metapage.io/{channel}",
data='{"temperature": 21.4}',
)Unlike a websocket send, a POST has no socket to exclude — every listener on the channel receives it.
A complete two-way client
Realistic clients reconnect, because networks and server deploys drop sockets. This is the whole of a production-shaped client:
/**
* Join a channel. Returns a `send` function; `onMessage` is called for every
* message from every other client on the channel.
*/
export const joinChannel = (channel, onMessage, origin = location.origin) => {
const url = new URL(`/${channel}`, origin);
url.protocol = url.protocol.replace("http", "ws");
let ws;
let queue = [];
const connect = () => {
ws = new WebSocket(url);
ws.addEventListener("open", () => {
queue.splice(0).forEach((message) => ws.send(message));
});
ws.addEventListener("message", (event) => onMessage(event.data));
ws.addEventListener("close", () => setTimeout(connect, 500));
ws.addEventListener("error", () => ws.close());
};
connect();
return (message) => {
if (ws.readyState === WebSocket.OPEN) ws.send(message);
else queue.push(message);
};
};const send = joinChannel(channel, (data) => console.log("peer:", data));
send(JSON.stringify({ cursor: { x: 10, y: 20 } }));If you would rather not own that logic, reconnecting-websocket is a drop-in WebSocket replacement and is what this project's own pages use.
Running it locally
git clone https://github.com/metapages/websocket-router
cd websocket-router
docker run -d -p 6379:6379 redis # redis is required
just dev # http://localhost:3077Then connect to ws://localhost:3077/my-test-channel. See Self-hosting for the full setup.