← back to gallery

Postgres Branch Board

Measured branch-creation latency for every Postgres branching service — Neon, Supabase, Xata, Ardent, more

dev-toolspostgrescibranchingneonsupabasebenchmarkcomparison
Open product ↗

postgres-branch-board

Comparison + transparently-dated benchmark of every commercial Postgres-branching service: Neon, Supabase Branching, Xata, Tembo, Ardent, Crunchy Bridge, Nile.

For the backend dev or CTO Googling "Neon vs Supabase branches" while wiring CI ephemeral DBs and trying to pick the one that won't cost $400/mo at 100 PRs.

The board shows one timed run of branch-creation latency (with the measurement date next to every cell), plus live GitHub stars / last-commit / HN traction / pricing-page heartbeat fetched by node-cron.

What's measured vs. what's live

| Source | Provenance | Refresh |
|--------|-----------|---------|
| Branch-creation latency, free-tier limits, cold-start, fork-from-prod, regions, price-at-scale | One-shot snapshot, 2026-05-14, every row carries source_url | Manual re-run; UI labels them as snapshot |
| Last funding round, amount, date | Snapshot, source URL per entry | Manual |
| GitHub repo: stars, forks, last commit | api.github.com/repos/{org}/{cli-repo} | hourly cron (cron expr 7 ) |
| HN news per provider | hn.algolia.com/api/v1/search?query=<provider>&tags=story | daily cron (17 5 ) |
| Pricing-page heartbeat (HTTP status + SHA-256 of body) | Each provider's public pricing URL | daily cron (37 6
) |
| Docs-page heartbeat (status, keyword spot-checks) | Each provider's branching docs URL | weekly cron (47 7 1) |

There is no mock data. If a metric had no public source URL it was dropped — better one honest row than ten fake ones.

Endpoints (all under /postgres-branch-board)

| Method | Path | Purpose |
|--------|------|---------|
| GET | /health | {ok:true} — no auth |
| GET | / | SPA — board + filter chips + sortable table |
| GET | /p/:slug | Server-rendered scorecard shell (OG tags point at badge SVG) |
| GET | /api/providers | Full board (merged measured + live + badges) |
| GET | /api/providers/:slug | Single provider deep view (metrics, news, fetch state) |
| GET | /api/news?limit=50 | Newest HN items across all providers |
| GET | /api/fetch-log?limit=100 | Recent fetch attempts with status + duration |
| GET | /api/freshness | Latest fetch timestamp per source |
| POST | /api/refresh?source=github\|hn\|pricing\|docs\|all | Trigger ad-hoc fetch — no auth, rate-limited 1/min per source |
| GET | /api/badges/:slug.svg | Shareable 600×120 SVG badge (dark, vendor-friendly) |

Real data source URLs

GitHub (hourly)

A GITHUB_TOKEN raises the rate limit from 60 to 5000 req/hour. Without it the hourly cron still works.

HN Algolia (daily)

Pricing pages (daily heartbeat)

Docs pages (weekly heartbeat)

If a docs/pricing URL 404s the heartbeat still records the status — that's the point. The UI shows it as an amber dot rather than pretending the page exists.

"Best for: X" rules (deterministic, no randomness)

Computed at request time from the merged board snapshot.

A provider can hold 0..N badges; the board shows the top inline + the rest in a tooltip.

Running locally

# Node 22+ required (better-sqlite3 prebuilts)
cp .env.example .env
npm install
npm start
# → http://localhost:4771/postgres-branch-board/

The server seeds providers + measured metrics on boot and kicks off one full live-signal fetch in the background so the board has data immediately. Subsequent live data is refreshed on the cron schedules above (or manually via POST /api/refresh).

To skip the initial bootstrap fetch (faster boots during dev):

SKIP_BOOTSTRAP_FETCH=1 npm start

Config knobs

All read from env, all optional except PORT if you want a non-default port.

| Var | Default | Purpose |
|-----|---------|---------|
| PORT | 4771 | Listen port |
| BASE_PATH | /postgres-branch-board | URL mount prefix |
| DB_PATH | ./data/board.db | better-sqlite3 file (WAL mode) |
| GITHUB_TOKEN | unset | Optional; raises GitHub rate limit |
| CRON_GITHUB / CRON_HN / CRON_PRICING / CRON_DOCS | see above | Override cron schedules |
| SKIP_BOOTSTRAP_FETCH | unset | Set to 1 to skip the boot-time fetch |

File layout

server.js               # express bootstrap, helmet, compression
db.js                   # better-sqlite3 init + schema + read/write helpers
config.js               # port, base path, cron schedules
scoring.js              # "Best for: X" badge rules
scheduler.js            # node-cron jobs + ad-hoc runOnce
scrapers/
  http.js               # abortable fetch with shared UA
  github.js             # repo + last-commit, ETag-aware
  hn.js                 # Algolia search + dedupe by objectID
  pricing.js            # HEAD/GET pricing, sha256 the body, baseline diff
  docs.js               # GET docs, sha + keyword spot-check
routes/
  api.js                # board endpoints + /api/refresh
  badges.js             # /api/badges/:slug.svg renderer
  scorecard.js          # /p/:slug SSR shell with OG meta
data/
  providers.json        # 7 providers, config
  measured_metrics.json # 7 × 7 metric snapshot, each row has source_url + measured_at
public/
  index.html            # SPA shell
  app.js                # board + scorecard + news + debug log
  scorecard.js          # standalone bundle for /p/:slug shell
  style.css             # dark theme, ≈200 LOC

Honesty notes

License

MIT.