Skip to content

Yjs document

This example exposes a Yjs-backed document API. Clients send base64 Yjs updates, sync from a base64 state vector, publish awareness, and compact hot state after a document has many updates.

Terminal window
import { backend } from "@layeron/core"
import { realtime } from "@layeron/modules"
const app = backend()
const live = realtime({
name: "docs",
historyLimit: 500,
lifecycle: {
idleTtlSeconds: 300,
destroyTtlSeconds: 86400,
},
})
app.use(live)
Terminal window
app.post("/documents/:documentId/yjs/updates", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const documentId = pathSegments[2]
const body = await request.json()
return await live.crdtRoom(documentId).applyYjsUpdate({
updateBase64: body.updateBase64,
clientId: body.clientId,
clock: body.clock,
})
})
Terminal window
app.post("/documents/:documentId/yjs/sync", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const documentId = pathSegments[2]
const body = await request.json()
return await live.crdtRoom(documentId).syncYjs({
stateVectorBase64: body.stateVectorBase64,
limit: 200,
})
})
Terminal window
app.post("/documents/:documentId/yjs/awareness", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const documentId = pathSegments[2]
const body = await request.json()
return await live.crdtRoom(documentId).awareness({
clientId: body.clientId,
state: {
cursor: body.cursor,
selection: body.selection,
color: body.color,
displayName: body.displayName,
},
})
})
Terminal window
app.post("/documents/:documentId/yjs/compact", async (request) => {
const pathSegments = new URL(request.url).pathname.split("/")
const documentId = pathSegments[2]
return await live.crdtRoom(documentId).compact()
})

compact() returns a checkpoint update and state vector. Active clients can keep syncing with syncYjs().