PostgreSQL and migrations
@rungsjs/store-postgres keeps requests, the audit trail, the outbox and the inbox in PostgreSQL
15 or newer. It has no dependencies of its own: you pass the driver you already use.
export async function connect(connectionString: string): Promise<Rungs> { const db = fromPg(new pg.Pool({ connectionString, max: 10 })); // more than one connection await migrate(db); // creates or upgrades the tables; safe to run from several processes return createRungs({ store: postgresStore(db), clock: () => new Date(), ids: () => crypto.randomUUID(), });}Runs in CI:examples/docs-snippets/src/guides/postgres.ts
| Driver | Adapter |
|---|---|
node-postgres (pg) |
fromPg(pool) |
| postgres.js | fromPostgresJs(sql) |
| PGlite (in-process, for tests and demos) | fromPglite(db) |
The pool needs more than one connection: callbacks such as roles run while a command holds its
connection. A single-connection database such as PGlite works when callbacks do not query it.
Migrations
Section titled “Migrations”migrate(db) applies missing migrations in one transaction under an advisory lock, so several
processes can start at once. Migrations only go forward. A store refuses to work on an older
schema and tells you to run migrate(), so a forgotten migration fails loudly.
Tables live in schema public with prefix rungs_ by default:
const approvals = fromPglite(await PGlite.create());await migrate(approvals, { schema: "approvals", tablePrefix: "wf_" });const store = postgresStore(approvals, { schema: "approvals", tablePrefix: "wf_" });Runs in CI:examples/docs-snippets/src/guides/postgres.ts
If your migrations run through another tool, generate the SQL instead. Each migration records its
version, so migrate() and the version check keep working afterwards:
// For Prisma, Drizzle, Flyway or plain psql: the same migrations as SQL.const everything = migrationSql({ schema: "approvals" });const upgradeFrom1 = migrationSql({ schema: "approvals", from: 1 });Runs in CI:examples/docs-snippets/src/guides/postgres.ts
The same scripts ship in the package as migrations/0001_init.sql, 0002_list_queries.sql and
so on.
Concurrency
Section titled “Concurrency”Commands lock the request row and save with a compare-and-set on its version. tick and event
delivery claim rows with FOR UPDATE SKIP LOCKED, so several workers can run side by side without
doing the same work; correctness never depends on that lease
(EC-27). The store is tested against PostgreSQL 15, 16, 17 and 18 with
both node-postgres and postgres.js.