A minimal starter template for a self-hosted Postgres + PowerSync + TanStack DB stack.
This repo uses the same shape as the PowerSync workbench examples:
- Dockerized PostgreSQL with logical replication enabled
- A self-hosted PowerSync service pointed at that database
- A custom backend that issues PowerSync JWTs and applies queued writes
- A Vite + React + TanStack DB frontend backed by PowerSync local SQLite
- Frontend: React 19 + Vite + TypeScript
- Local-first sync:
@powersync/web+@powersync/react - Reactive collections:
@tanstack/react-db+@tanstack/powersync-db-collection - Backend: Express + PostgreSQL
- Infra: Docker Compose for Postgres and PowerSync
- Copy the local environment file:
cp .env.example .env- Install dependencies:
pnpm install- Start PostgreSQL + PowerSync:
pnpm db:up- Start the frontend and backend:
pnpm dev- Open
http://localhost:5173
- React app:
http://localhost:5173 - Backend API:
http://localhost:3001 - PowerSync:
http://localhost:8080 - Postgres:
localhost:5432
pnpm db:logs # Follow Postgres + PowerSync logs
pnpm db:down # Stop services and remove volumes
pnpm db:reset # Recreate the database from scratch
pnpm typecheck # Run TypeScript checks for app + server
pnpm build # Build the app + server
pnpm powersync:token- The frontend creates TanStack DB collections on top of a PowerSync-backed SQLite database.
fetchCredentials()asks the backend for a PowerSync JWT.- PowerSync syncs
listsandtodosfrom Postgres using Sync Streams. - Local writes are queued in SQLite.
uploadData()posts queued CRUD operations to the backend.- The backend writes them to Postgres in a transaction.
- PowerSync replicates those changes back down to every client.
.
├── app/ # React + TanStack DB frontend
├── postgres/init/ # Postgres schema + seed data
├── powersync/ # PowerSync self-hosted config
├── server/ # Token endpoint + upload API
└── docker-compose.yml # Local Postgres + PowerSync stack
- The template uses a development HS256 key so it stays copy-paste runnable. Replace it with your real auth flow before shipping.
- The upload endpoint uses a strict table allowlist, but it still assumes a trusted local client. Add real authentication and authorization in production.
- Keep your Postgres schema,
powersync/sync-config.yaml, and the client schema inapp/src/lib/powersync/schema.tsaligned.