Hooks
Using hooks, you can define a WebSocket server that works across runtimes with the same syntax.
Crossws provides a cross-platform API to define WebSocket servers. An implementation with these hooks works across runtimes without needing you to go into details of each of them. You only define the life-cycle hooks that you only need.
Using
defineHooks() wrapper is optional and for type support and code auto completion.import { defineHooks } from "crossws";
const hooks = defineHooks({
upgrade(req) {
console.log(`[ws] upgrading ${req.url}...`);
return {
// namespace: new URL(req.url).pathname
headers: {},
};
},
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, details) {
console.log("[ws] close", peer, details);
},
error(peer, error) {
console.log("[ws] error", peer, error);
},
drain(peer) {
// Send buffer drained after backpressure — safe to resume sending.
// Pair with `peer.bufferedAmount`. Not all adapters emit this.
console.log("[ws] drain", peer);
},
});
Context
You can attach data to the connection by returning a context object from the upgrade hook. This data is then available as peer.context in all other hooks for the lifetime of the connection.
Context can be volatile in some environments like
cloudflare-durable.import { defineHooks } from "crossws";
const hooks = defineHooks({
upgrade(req) {
return {
context: { data: "myData" },
};
},
open(peer) {
console.log(peer.context.data); // myData
},
message(peer, message) {
console.log(peer.context.data); // myData
},
close(peer, details) {
console.log(peer.context.data); // myData
},
});
Authentication
During the upgrade hook it is possible to authenticate the user before upgrading the connection. If the user is not authenticated, you can return (or throw) a Response to prevent the connection from being upgraded.
import { defineHooks } from "crossws";
const hooks = defineHooks({
upgrade(req) {
const authHeader = req.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Basic ")) {
return new Response("Unauthorized", {
status: 401,
headers: {
"WWW-Authenticate":
'Basic realm="Websocket Authentication", charset="UTF-8"',
},
});
}
const base64Credentials = authHeader.split(" ")[1];
const [username, password] = atob(base64Credentials).split(":");
if (username !== "myUsername" || password !== "myPassword") {
return new Response("Unauthorized", {
status: 401,
headers: {
"WWW-Authenticate":
'Basic realm="Websocket Authentication", charset="UTF-8"',
},
});
}
return {
headers: {}, // Optionally return custom headers
};
},
});