{"id":16590,"library":"y-postgresql","title":"Yjs PostgreSQL Persistence","description":"y-postgresql is a community-maintained PostgreSQL database adapter designed to provide persistent storage for Yjs documents, commonly used in conjunction with a `y-websocket` server for real-time collaborative applications. As of version `1.0.1`, it offers features for storing Yjs updates and retrieving full document states from PostgreSQL. The package differentiates itself by providing a robust, battle-tested persistence solution for PostgreSQL users within the Yjs ecosystem, handling the serialization and deserialization of Yjs document updates directly. While it is not officially supported by the Yjs core team, it maintains compatibility with recent Yjs versions and provides configurable options like table naming, flush size for merging updates, and indexing for performance tuning. Release cadence is independent of Yjs core, typically driven by community contributions and specific feature requirements or bug fixes related to PostgreSQL integration.","status":"active","version":"1.0.1","language":"javascript","source_language":"en","source_url":"https://github.com/MaxNoetzold/y-postgresql","tags":["javascript","yjs","postgresql","database","adapter","shared editing","collaboration","offline","CRDT","typescript"],"install":[{"cmd":"npm install y-postgresql","lang":"bash","label":"npm"},{"cmd":"yarn add y-postgresql","lang":"bash","label":"yarn"},{"cmd":"pnpm add y-postgresql","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core collaborative editing library that y-postgresql provides persistence for.","package":"yjs","optional":false}],"imports":[{"note":"The library is primarily ESM-first, requiring Node.js 16+ for direct `import` statements. Attempting to `require` it in a CommonJS context without proper transpilation or configuration will fail.","wrong":"const PostgresqlPersistence = require('y-postgresql');","symbol":"PostgresqlPersistence","correct":"import { PostgresqlPersistence } from 'y-postgresql';"},{"note":"Type import for configuring the persistence instance, useful for TypeScript projects.","symbol":"PostgresqlPersistenceOptions","correct":"import type { PostgresqlPersistenceOptions } from 'y-postgresql';"},{"note":"Type import for configuring the underlying PostgreSQL connection, based on `pg.PoolConfig`.","symbol":"PostgresqlConnectionOptions","correct":"import type { PostgresqlConnectionOptions } from 'y-postgresql';"}],"quickstart":{"code":"import 'dotenv/config';\nimport http from 'http';\nimport { WebSocketServer } from 'ws';\nimport * as Y from 'yjs';\n// Assuming these utilities are available from y-websocket or a local setup\nimport { setPersistence, setupWSConnection } from './websocket/utils.js'; \nimport { PostgresqlPersistence } from 'y-postgresql';\n\nconst server = http.createServer((request, response) => {\n\tresponse.writeHead(200, { 'Content-Type': 'text/plain' });\n\tresponse.end('okay');\n});\n\nconst wss = new WebSocketServer({ server });\nwss.on('connection', setupWSConnection); // Use the y-websocket setup utility\n\nasync function startPersistence() {\n    const pgdb = await PostgresqlPersistence.build(\n        {\n            host: process.env.PG_HOST ?? 'localhost',\n            port: parseInt(process.env.PG_PORT ?? '5432', 10),\n            database: process.env.PG_DATABASE ?? 'yjs_db',\n            user: process.env.PG_USER ?? 'postgres',\n            password: process.env.PG_PASSWORD ?? '',\n        },\n        { tableName: 'yjs-documents', useIndex: true, flushSize: 200 },\n    );\n\n    setPersistence({\n        bindState: async (docName, ydoc) => {\n            const persistedYdoc = await pgdb.getYDoc(docName);\n            Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc));\n            ydoc.on('update', async (update: Uint8Array) => {\n                pgdb.storeUpdate(docName, update);\n            });\n        },\n        writeState: async (docName, ydoc) => {\n            // Optional: Ensure all updates are flushed before document destroy\n            return new Promise((resolve) => resolve());\n        },\n    });\n    \n    server.listen(process.env.PORT ?? 8080, () => {\n        console.log(`y-websocket server with y-postgresql persistence listening on port: ${process.env.PORT ?? 8080}`);\n    });\n}\n\nstartPersistence().catch(console.error);\n","lang":"typescript","description":"This example sets up a basic `y-websocket` server and integrates `y-postgresql` for persistent storage of Yjs documents in a PostgreSQL database, demonstrating connection, state binding, and update storage."},"warnings":[{"fix":"Review the source code and consider the long-term maintenance implications before relying on it for critical applications. Contributions and community support are essential.","message":"This package is not officially supported by the Yjs team, meaning ongoing maintenance and compatibility with future Yjs versions are not guaranteed by the core Yjs developers.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Upgrade your Node.js environment to version 16 or higher.","message":"The package requires Node.js version 16 or newer. Running on older Node.js versions will result in runtime errors due to reliance on newer JavaScript features and module resolution.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"For better read performance, especially with a large number of documents, consider setting `useIndex: true` in the `PostgresqlPersistence.build` options. Be aware that this might slightly increase write times.","message":"The default `useIndex: false` for the PostgreSQL table means that read operations, especially `getYDoc`, might become slow for applications with many documents or large update histories, as no index is created on the `docname` column.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Always ensure that `ydoc.on('update', async (update: Uint8Array) => { pgdb.storeUpdate(docName, update); })` is correctly implemented within `bindState` to persist granular updates as they occur.","message":"Properly implementing the `bindState` and `writeState` methods within the `setPersistence` callback is crucial for ensuring data durability. Forgetting to listen to `ydoc.on('update', ...)` within `bindState` can lead to data loss if the server crashes before updates are flushed.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure your `tsconfig.json` has `\"module\": \"NodeNext\"` or `\"ESNext\"` and `\"moduleResolution\": \"NodeNext\"`. Also, confirm Node.js version 16 or newer is in use. For JavaScript, ensure your file uses `.mjs` extension or your `package.json` has `\"type\": \"module\"`.","cause":"Incorrect module resolution for an ESM-only package in a CommonJS context, or missing Node.js 16+ environment. Could also be a TypeScript configuration issue.","error":"Cannot find module 'y-postgresql' or its corresponding type declarations."},{"fix":"Always use `await PostgresqlPersistence.build(...)` to create an instance, as it is an asynchronous factory method, not a direct constructor.","cause":"Attempting to instantiate `PostgresqlPersistence` directly with `new` instead of using the asynchronous static `build` factory method.","error":"TypeError: PostgresqlPersistence.build is not a function"},{"fix":"Verify that your PostgreSQL server is running and accessible from the application. Double-check all connection options passed to `PostgresqlPersistence.build`, especially environment variables like `PG_HOST`, `PG_PORT`, `PG_USER`, `PG_PASSWORD`, and `PG_DATABASE`.","cause":"The PostgreSQL database server is not running, is inaccessible from the application's host, or the connection parameters (host, port, user, password, database) are incorrect.","error":"Error: connect ECONNREFUSED ::1:5432 (or similar database connection error)"}],"ecosystem":"npm"}