yjs websocket server powered by crossws, works with Node.js, Deno, Bun, Cloudflare Workers and more without any framework dependency and compatible with unmodified y-websocket client provider.
Note
๐ง This is still a work in progress. Feedback and contributions are welcome!
Note
See nuxt-tiptap for a full Demo with Nuxt.
tiptap editor with collaborative editing deployed on Cloudflare workers with durable objects.
We first need to initiate universal cross-hooks:
import { createHandler } from "y-crossws";
const crosswsHandler = createHandler();
Depending on your server choice, use any of the crossws supported adapters.
import { createServer } from "node:http";
import { createHandler } from "y-crossws";
import crossws from "crossws/adapters/node";
const server = createServer((req, res) => {
res.statusCode = 426;
res.end("");
});
const ws = crossws(createHandler());
server.on("upgrade", ws.handleUpgrade);
server.listen(3000);
Note
Read more about Node.js adapter in crossws docs.
import { createHandler } from "y-crossws";
import crossws from "crossws/adapters/bun";
const ws = crossws(createHandler());
Bun.serve({
port: 3000,
websocket: ws.websocket,
fetch(req, server) {
if (request.headers.get("upgrade") === "websocket") {
return ws.handleUpgrade(request, server);
}
return new Response("", { status: 426 });
},
});
Note
Read more about Bun adapter in crossws docs.
import { createHandler } from "y-crossws";
import crossws from "crossws/adapters/deno";
const ws = crossws(createHandler());
Deno.serve({ port: 3000 }, (request, info) => {
if (request.headers.get("upgrade") === "websocket") {
return ws.handleUpgrade(request, server);
}
return new Response("", { status: 426 });
});
Note
Read more about Deno adapter in crossws docs.
import { createHandler } from "y-crossws";
import { DurableObject } from "cloudflare:workers";
import crossws from "crossws/adapters/cloudflare-durable";
const ws = crossws(createHandler());
export default {
async fetch(request, env, context) {
if (request.headers.get("upgrade") === "websocket") {
return ws.handleUpgrade(request, env, context);
}
return new Response("", { status: 426 });
},
};
export class $DurableObject extends DurableObject {
fetch(request) {
return ws.handleDurableUpgrade(this, request);
}
webSocketMessage(client, message) {
return ws.handleDurableMessage(this, client, message);
}
webSocketClose(client, code, reason, wasClean) {
return ws.handleDurableClose(this, client, code, reason, wasClean);
}
}
Update your wrangler.toml
config to specify Durable object:
durable_objects.bindings = [
{ name = "$DurableObject", class_name = "$DurableObject" }
]
migrations = [
{ tag = "v1", new_classes = ["$DurableObject"] }
]
Note
Read more about Cloudflare adapter in crossws docs.
You can use WebsocketProvider
from legacy y-websocket or a native one from y-crossws
. Both are almost identical in terms of API at the moment, however, the y-crossws version has better typescript refactors and might introduce more enhancements in sync with the server provider in the future.
import * as Y from "yjs";
import { WebsocketProvider } from "y-crossws/provider";
const ydoc = new Y.Doc();
const roomName = "default";
const wsProto = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${wsProto}://${window.location.host}/_ws`;
const provider = new WebsocketProvider(wsURL, roomName, ydoc, {
/* options */
});
params
: URL parameters to append.protocols
: Specify websocket protocols.WebSocketPolyfill
: WebSocket polyfill.maxBackoffTime
: Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential (default2500
).resyncInterval
: Request server state everyresyncInterval
milliseconds (default-1
).connect
: Whether to connect to other peers or not (default:true
).awareness
: Awareness instance.disableBc
: Disable cross-tab BroadcastChannel communication (default:false
).
local development
๐ Published under the MIT license.