Warning
This project is experimental and is not yet recommended for production use.
y-partyserver
is an addon library for partyserver
designed to host backends for Yjs, a high-performance library of data structures for building collaborative software.
This document assumes some familiarity with Yjs. If you're new to Yjs, you can learn more about it in the official documentation.
Like PartyServer, YServer
is a class that extends DurableObject
(as well as PartyServer's Server
). The simplest Yjs backend can be set up like so:
export { YServer as MyYServer } from "y-partyserver";
// then setup wrangler.toml and a default fetch handler
// like you would for PartyServer.
Use the provider to connect to this server from your client:
import YProvider from "y-partyserver/provider";
import * as Y from "yjs";
const yDoc = new Y.Doc();
const provider = new YProvider("localhost:8787", "my-document-name", yDoc);
You can add additional options to the provider:
const provider = new YProvider(
/* host */
"localhost:8787",
/* document/room name */
"my-document-name",
/* Yjs document instance */
yDoc,
{
/* whether to connect to the server immediately */
connect: false,
/* the party server path to connect to, defaults to "main" */
party: "my-party",
/* the path to the Yjs document on the server
* This replaces the default path of /parties/:party/:room.
*/
prefix: "/my/own/path",
/* use your own Yjs awareness instance */
awareness: new awarenessProtocol.Awareness(yDoc),
/* query params to add to the websocket connection
* This can be an object or a function that returns an object
*/
params: async () => ({
token: await getAuthToken()
}),
/* the WebSocket implementation to use
* This can be a polyfill or a custom implementation
*/
WebSocketPolyfill: WebSocket,
/* the interval at which to resync the document
* This is set to -1 by default to disable resyncing by polling
*/
resyncInterval: -1,
/* Maximum amount of time to wait before trying to reconnect
* (we try to reconnect using exponential backoff)
*/
maxBackoffTimeout: 2500,
/* Disable cross-tab BroadcastChannel communication */
disableBc: false
}
);
If you're using React, then you can use the hook version of the provider: useYProvider
.
import useYProvider from "y-partyserver/react";
function App() {
const provider = useYProvider({
host: "localhost:8787", // optional, defaults to window.location.host
room: "my-document-name",
party: "my-party", // optional, defaults to "main"
doc: yDoc, // optional!
options
});
}
By default, PartyKit maintains a copy of the Yjs document as long as at least one client is connected to the server. When all clients disconnect, the document state may be lost.
To persist the Yjs document state between sessions, you can configure onLoad and onSave methods:
// server.ts
import { YServer } from "y-partyserver";
export class MyDocument extends YServer {
/* control how often the onSave handler
* is called with these options */
static callbackOptions = {
// all of these are optional
debounceWait: /* number, default = */ 2000,
debounceMaxWait: /* number, default = */ 10000,
timeout: /* number, default = */ 5000
};
async onLoad() {
// load a document from a database, or some remote resource
// and apply it on to the Yjs document instance at `this.document`
const content = (await fetchDataFromExternalService(
this.name
)) as Uint8Array;
if (content) {
Y.applyUpdate(this.document, content);
}
return;
}
async onSave() {
// called every few seconds after edits, and when the room empties
// you can use this to write to a database or some external storage
await sendDataToExternalService(
this.name,
Y.encodeStateAsUpdate(this.document) satisfies Uint8Array
);
}
}
onLoad
is called once when a client connects to the server. It should initialise the Yjs document instance at this.document
. Once the document has been loaded, it's kept in memory until the session ends.
onSave
is called periodically after the document has been edited, and when the room is emptied. It should be used to save the document state to a database or some other external storage.
For more information, refer to the official Yjs documentation. Examples provided in the Yjs documentation should work seamlessly with y-partyserver
(ensure to replace y-websocket
with y-partyserver/provider
).
readonly mode