JSPM

  • Created
  • Published
  • Downloads 1175
  • Score
    100M100P100Q113957F
  • License MIT

PGlite-powered zero-sync development backend. No Docker required.

Package Exports

  • orez
  • orez/s3
  • orez/vite

Readme

orez

Zero development backend powered by PGlite and bedrock-sqlite (our WASM fork of SQLite's bedrock branch). Bundles PostgreSQL and zero-cache into a single package with no native dependencies — both Postgres and SQLite run as WASM, so you can bunx orez and have a full backend up in seconds. No Docker, no Postgres install, no node-gyp, no platform-specific binaries.

bunx orez

Starts PGlite (Postgres via WASM), the TCP proxy, and zero-cache (SQLite via WASM). Ports auto-increment if already in use.

Exports a CLI, programmatic API, and Vite plugin.

Install

bun install orez

@rocicorp/zero is included as a dependency and provides the zero-cache binary.

CLI

bunx orez
--pg-port=6434            postgresql proxy port
--zero-port=5849          zero-cache port
--data-dir=.orez          data directory
--migrations=DIR          migrations directory (skipped if not set)
--seed=FILE               seed file path
--pg-user=user            postgresql user
--pg-password=password    postgresql password
--skip-zero-cache         run pglite + proxy only, skip zero-cache
--log-level=info          error, warn, info, debug
--s3                      also start a local s3-compatible server
--s3-port=9200            s3 server port
--disable-wasm-sqlite     use native @rocicorp/zero-sqlite3 instead of wasm bedrock-sqlite
--on-db-ready=CMD         command to run after db+proxy are ready, before zero-cache starts
--on-healthy=CMD          command to run once all services are healthy

Subcommands for standalone servers:

bunx orez s3 --port=9200 --data-dir=.orez

Programmatic

import { startZeroLite } from 'orez'

const { config, stop } = await startZeroLite({
  pgPort: 6434,
  zeroPort: 5849,
  migrationsDir: 'src/database/migrations',
  seedFile: 'src/database/seed.sql',
})

// your app connects to zero-cache at localhost:5849
// database is at postgresql://user:password@localhost:6434/postgres

// when done
await stop()

All options are optional with sensible defaults. Ports auto-find if in use.

Vite plugin

import orez from 'orez/vite'

export default {
  plugins: [
    orez({
      pgPort: 6434,
      zeroPort: 5849,
      migrationsDir: 'src/database/migrations',
    }),
  ],
}

Starts orez when vite dev server starts, stops on close.

How it works

orez starts three things:

  1. A PGlite instance (full PostgreSQL 16 running in-process via WASM)
  2. A TCP proxy that speaks the PostgreSQL wire protocol, including logical replication
  3. A zero-cache child process that connects to the proxy thinking it's a real Postgres server

The trick is in the TCP proxy. zero-cache needs logical replication to stay in sync with the upstream database. PGlite doesn't support logical replication natively, so orez fakes it. Every mutation is captured by triggers into a changes table, then encoded into the pgoutput binary protocol and streamed to zero-cache through the replication connection. zero-cache can't tell the difference.

The proxy also handles multi-database routing. zero-cache expects three separate databases (upstream, CVR, change), but PGlite is a single database. orez maps database names to schemas, so zero_cvr becomes the zero_cvr schema and zero_cdb becomes zero_cdb.

Zero native dependencies

The whole point of orez is that bunx orez works everywhere with no native compilation step. Postgres runs in-process as WASM via PGlite. But zero-cache also needs SQLite, and @rocicorp/zero-sqlite3 ships as a compiled C addon — which means node-gyp, build tools, and platform-specific binaries.

orez ships its own package, bedrock-sqlite — SQLite's bedrock branch recompiled to WASM with BEGIN CONCURRENT and WAL2 support. At startup, orez patches @rocicorp/zero-sqlite3 to load bedrock-sqlite instead of the native C addon. Both databases run as WASM — nothing to compile, nothing platform-specific. Just bun install and go.

Environment variables

Your entire environment is forwarded to the zero-cache child process. This means any ZERO_* env vars you set are passed through automatically.

orez provides sensible defaults for a few variables:

Variable Default Overridable
NODE_ENV development yes
ZERO_LOG_LEVEL from --log-level yes
ZERO_NUM_SYNC_WORKERS 1 yes
ZERO_UPSTREAM_DB (managed by orez) no
ZERO_CVR_DB (managed by orez) no
ZERO_CHANGE_DB (managed by orez) no
ZERO_REPLICA_FILE (managed by orez) no
ZERO_PORT (managed by orez) no

The --log-level flag controls both zero-cache (ZERO_LOG_LEVEL) and PGlite's debug output. Setting it to debug enables verbose logging from both.

The layering is: orez defaults → your env → orez-managed connection vars. So setting ZERO_LOG_LEVEL=debug in your shell overrides the --log-level default, but you can't override the database connection strings (orez needs to point zero-cache at its own proxy).

Common vars you might want to set:

ZERO_MUTATE_URL=http://localhost:3000/api/zero/push
ZERO_QUERY_URL=http://localhost:3000/api/zero/pull

What gets faked

The proxy intercepts several things to convince zero-cache it's talking to a real PostgreSQL server with logical replication enabled:

  • IDENTIFY_SYSTEM returns a fake system ID and timeline
  • CREATE_REPLICATION_SLOT persists slot info in a local table and returns a valid LSN
  • START_REPLICATION enters streaming mode, encoding changes as pgoutput binary messages
  • current_setting('wal_level') always returns logical
  • pg_replication_slots queries are redirected to a local tracking table
  • SET TRANSACTION SNAPSHOT is silently accepted (PGlite doesn't support imported snapshots)
  • ALTER ROLE ... REPLICATION returns success
  • READ ONLY is stripped from transaction starts to avoid PGlite serialization issues

The pgoutput encoder produces spec-compliant binary messages: Begin, Relation, Insert, Update, Delete, Commit, and Keepalive. All column values are encoded as text (typeOid 25), which zero-cache handles fine since it re-maps types downstream anyway.

Tests

141 tests — 104 orez tests across 7 test files covering the full stack from binary encoding to TCP-level integration, plus 37 bedrock-sqlite tests covering the WASM SQLite engine:

bun run test                                # orez tests
cd sqlite-wasm && bunx vitest run           # bedrock-sqlite tests

The orez test suite includes a zero-cache compatibility layer that decodes pgoutput messages into the same typed format that zero-cache's PgoutputParser produces, validating end-to-end compatibility.

The bedrock-sqlite tests cover Database/Statement API, transactions, WAL/WAL2 modes, BEGIN CONCURRENT, FTS5, JSON functions, custom functions, aggregates, bigint handling, and file persistence.

Limitations

This is a development tool. It is not suitable for production use.

  • PGlite is single-connection. All queries are serialized through a mutex. Fine for development but would bottleneck under real load.
  • Column types are all encoded as text in the replication stream. Zero-cache handles this, but other pgoutput consumers might not.
  • Triggers add overhead to every write. Again, fine for development.
  • PGlite stores data on the local filesystem. No replication, no backups, no high availability.

Project structure

src/
  index.ts              main entry, orchestrates startup + sqlite wasm patching
  cli.ts                cli with citty
  config.ts             configuration with defaults
  log.ts                colored log prefixes
  port.ts               auto port finding
  pg-proxy.ts           tcp proxy with query rewriting
  pglite-manager.ts     pglite instance and migration runner
  s3-local.ts           local s3-compatible server (orez/s3)
  vite-plugin.ts        vite dev server plugin (orez/vite)
  replication/
    handler.ts          replication protocol state machine
    pgoutput-encoder.ts binary pgoutput message encoder
    change-tracker.ts   trigger installation and change reader
sqlite-wasm/
  Makefile              emscripten build for bedrock-sqlite wasm binary
  bedrock-sqlite.d.ts   typescript declarations
  native/
    api.js              better-sqlite3 compatible database/statement API
    vfs.c               custom VFS with SHM support for WAL/WAL2
    vfs.js              javascript VFS bridge
  test/
    database.test.ts    37 tests for the wasm sqlite engine

Extra: orez/s3

Local s3-compatible server for dev. Avoids needing Docker or MinIO.

import { startS3Local } from 'orez/s3'

const server = await startS3Local({
  port: 9200,
  dataDir: '.orez',
})

Or via CLI: bunx orez --s3 or standalone bunx orez s3.

Handles GET, PUT, DELETE, HEAD with CORS. Files stored on disk. No multipart, no ACLs, no versioning.

License

MIT