data-peek
Reference

CLI (data-peek doctor)

Run data-peek's Postgres schema checks from the terminal with npx, no install required

CLI: npx @data-peek/cli doctor

The @data-peek/cli npm package runs the same schema checks as Schema Intel in the app, from a terminal, against any Postgres database you can reach.

npx @data-peek/cli doctor postgres://user:pass@localhost:5432/app

With no connection string it reads DATABASE_URL.

data-peek doctor · app @ localhost:5432 · PostgreSQL 16.4 · 8 checks in 31 ms

✖ 1 invalid index
  The planner ignores these. Drop, then rebuild with CREATE INDEX CONCURRENTLY.

  idx_orders_email  on orders
    DROP INDEX "public"."idx_orders_email";
    -- Then rebuild with: CREATE INDEX CONCURRENTLY ...

▲ 2 foreign keys without a supporting index
  Deletes on the parent and joins over the key scan the whole child table.

  payments(invoice_id)
    CREATE INDEX "idx_payments_invoice_id" ON "public"."payments" ("invoice_id");
  memberships(invited_by)
    CREATE INDEX "idx_memberships_invited_by" ON "public"."memberships" ("invited_by");

● 1 bloated table
  Over a fifth of the rows are dead tuples. VACUUM reclaims them.

  events  41.2% dead · 380 MB
    VACUUM (ANALYZE, VERBOSE) "public"."events";

● 3 nullable foreign keys
  Fine when NULL means "no reference". Otherwise add NOT NULL to keep out orphans.

  memberships(invited_by), projects(created_by), events(user_id)

✔ clean  primary keys, duplicate indexes, unused indexes, vacuum

1 critical · 2 warnings · 4 info · 7 findings
Fix with a click: open app in data-peek → https://datapeek.dev

Grouped by check, most severe first. The reason is said once per group, every finding carries the SQL that fixes it, and the checks that passed are named too.

Checks

CheckWhat it findsSeverity
tables_without_pkTables with no primary keywarning
missing_fk_indexesForeign keys whose columns are not the leading columns of any indexwarning
duplicate_indexesIndexes with identical column lists, operator classes, and predicateswarning
unused_indexesIndexes over 1 MB with idx_scan = 0 since the last statistics resetinfo
invalid_indexesIndexes left invalid by a failed CREATE INDEX CONCURRENTLYcritical
bloated_tablesTables with over 20 % dead tuples, estimated from n_dead_tupinfo
never_vacuumedTables over 1,000 rows with no vacuum or analyze on recordinfo
nullable_fksForeign key columns that allow NULLinfo

Every check is a read-only query against the system catalogs. No extensions are needed, one connection is opened and closed, and nothing leaves your machine.

Options

FlagEffect
--checks <a,b,c>Run only the listed checks
--jsonPrint the raw report as JSON
--fail-on <level>Exit with code 1 if any finding is at or above info, warning, or critical
--no-colorPlain output. NO_COLOR in the environment does the same

Exit codes: 0 clean or below the threshold, 1 findings at or above --fail-on, 2 usage or connection error, or a check that could not run while --fail-on is set. A gate that passes because the checks never ran is not a gate.

As a CI gate

Fail a build when a migration drops a foreign key’s index or leaves an index invalid:

- name: Schema checks
  run: npx @data-peek/cli doctor "$DATABASE_URL" --fail-on warning

Database support

Postgres only for now. MySQL and SQL Server connection strings print a clear message rather than a partial answer. The desktop app runs schema checks on all three.

Same checks as the app

The CLI and the desktop app import the same check file from the shared package, so they can never disagree about what a finding means. Open the connection in data-peek and the same findings are in Schema Intel, with the fix one click away.

On this page