v0.3.0-beta.1MITdry run by default

Firebase ↔ PostgreSQL.

FireMigrate moves data both ways. Forward: Firestore, subcollections, Auth, and Storage metadata into ordinary PostgreSQL tables. Reverse: any Postgres tables back into Firestore. Nothing is written until you pass --write.

source path → table

usersusers
users/*/postsusers__posts
users/*/posts/*/commentsusers__posts__comments
firebase authfirebase_auth_users
storage objectsfirebase_storage_objects

01 — what is written

Every streamed row, including the nested ones.

Firestore collections

Each top-level collection becomes a table, upserted by document id. Every row also stores _path.

Nested subcollections

Every parent document is scanned. users/*/posts/*/comments becomes users__posts__comments, with _parent_id and _parent_path.

Auth users

Users go to firebase_auth_users. Password hashes are stored and never printed. This is not a Supabase auth.users import.

Storage metadata

Object name, size, content type, and checksum go to firebase_storage_objects. File bytes stay in Firebase.

Unseen fields

A field missing from the sample is not dropped. It is kept in the jsonb column _extra.

Agent table list

tables prints JSON of every destination table, its source path, columns, and read/write hints.

02 — for agents

Tell the agent which tables exist.

Run tables before an agent writes SQL. The manifest names every table, its Firebase source, parent table, columns, and a read/write hint. Do not invent table names.

tables
$ npx @ezekielreu6/firemigrate tables

{
  "version": 1,
  "tableCount": 2,
  "tables": [{
    "table": "users__posts",
    "sourcePath": "users/*/posts",
    "kind": "subcollection",
    "parentTable": "users",
    "primaryKey": "id"
  }]
}

03 — commands

Inspect, list, then write.

terminal
npx @ezekielreu6/firemigrate init
npx @ezekielreu6/firemigrate inspect
npx @ezekielreu6/firemigrate reverse
npx @ezekielreu6/firemigrate reverse --preview
npx @ezekielreu6/firemigrate migrate --dry-run
01 init [--force]

Creates firemigrate.config.json and .env.example. It never writes credentials, and it will not overwrite files unless you pass --force.

02 inspect [--sample=<n>]

Read-only. Counts collections, walks every document for subcollections, summarizes Auth, and samples Storage metadata.

03 tables

Prints the agent JSON manifest. Stdout is JSON only. Call only the table names in that list.

04 schema

Prints the proposed PostgreSQL schema. Nothing is applied.

05 migrate --dry-run

The default. Streams every row and reports counts. Nothing is written.

06 migrate --write

Creates missing tables and upserts every streamed row. Also requires "dryRun": false in the config.

07 verify

Compares PostgreSQL row counts with the rows that were streamed. A mismatch exits non-zero.

08 reverse

Inspect any PostgreSQL tables and propose Firestore mappings. Works even if the tables were not created by FireMigrate.

09 reverse --preview

Sample a few rows and show the proposed Firestore documents.

10 reverse --write

Push tables to Firestore. Requires Firebase credentials and "dryRun": false.

Skip a piece with --skip-auth, --skip-storage, or --skip-subcollections.

05 — reverse

PostgreSQL tables back to Firestore.

The reverse command inspects any Postgres tables, proposes collection names and document ids, and can write them back to Firestore. It reuses _path when present and merges _extra. Available on the beta tag.

reverse
npx @ezekielreu6/firemigrate@beta reverse --preview

04 — limits

What it will not pretend to do.

  • Subcollection scans stop at 20 levels. Deeper paths are reported and skipped.
  • Auth is a Postgres table, not a Supabase Auth import. Password login still needs a separate import.
  • Storage file bytes are not copied.
  • Existing tables are not altered. An older table will not gain _extra by itself.
  • Writes stay off until --write and "dryRun": false are both set.