ngx-zero playground

July 20, 2026 · View on GitHub

A fully functional integration harness for the ngx-zero public API against a real Zero backend: Postgres (Docker) → zero-cache → an embedded Hono API → this Angular app. The app imports only from the package entry point.

Architecture

┌────────────────────────────┐      ┌───────────────┐      ┌──────────────────┐
│ Vite :5173                 │      │ zero-cache    │      │ Postgres :5430   │
│  ├─ Angular playground     │◄────►│ :4848         │◄────►│ (docker,         │
│  └─ Hono /api/*            │      │               │ WAL  │  wal_level=      │
│      /api/login            │      │ forwards      │      │  logical)        │
│      /api/zero/query   ◄───┼──────┤ query/mutate  │      └──────────────────┘
│      /api/zero/mutate  ◄───┼──────┘               │
└────────────────────────────┘      └───────────────┘
  • Drizzle is the source of truth for the database schema (src/db/schema.ts). pnpm generate regenerates the Zero schema (src/zero/schema.gen.ts) via drizzle-zero.
  • Queries and mutators are shared registries (src/zero/queries.ts, src/zero/mutators.ts) — the same code runs optimistically in the browser and authoritatively in the API server.
  • Auth is a fake token flow: /api/login mints an HS256 JWT for any user name; the query/mutate endpoints verify it and build the ctx passed to queries and mutators.

Running it

From the repository root (requires Docker):

pnpm playground:db   # once: start Postgres, push the schema, seed
pnpm playground      # build ngx-zero, then run zero-cache + vite

Open http://localhost:5173.

What to try

  • Add / complete / delete issues — optimistic mutations synced through the real backend. Open two windows to watch them converge. The header's “Mutation” readout is driven by the pending signals on injectMutator.
  • Add a title containing “rollback” — a server-only rule rejects it after the optimistic apply succeeds; watch the row appear and roll back, and the mutator's error signal surface the rejection.
  • Delete someone else's issue — the owner check fails during the optimistic apply, before the server is ever involved.
  • Switch user — userID (and context) change, so provideZero closes and recreates the instance (watch the instance counter).
  • Rotate auth — a same-user token rotation reconnects in place via zero.connection.connect({auth}); the instance counter must not move.
  • 15s token — a short-lived token; after it expires the API returns 401 and Zero enters needs-auth (the withAuthRefresh open question in docs/DESIGN.md).
  • Show only mine — swaps the materialized query to queries.issue.mine, whose filter is applied server-side from the verified JWT context.

Housekeeping

  • pnpm --filter ngx-zero-playground db:setup re-runs push + seed.
  • docker compose down -v (in this directory) resets the database.
  • .env is checked in on purpose; nothing in it is secret.