The dump-data command connects to a live database, fetches rows from the specified tables, and generates a migration file containing UpsertData operations. The output format (Go or Starlark) is determined by the migration.format setting in your config file. Use this to seed reference or lookup data (e.g. country codes, roles, unit types) so the data is version-controlled and applied consistently across environments via the normal migration workflow.
Running morphic generate dump-data <table1> [table2 ...] generates a migration file that:
- Contains one
UpsertDataoperation per table - Uses
INSERT ... ON CONFLICT (pk) DO UPDATEat migration runtime - Automatically depends on the current DAG leaves so it slots into the migration chain correctly
- Detects primary keys from the migration SchemaState (reconstructed from existing migrations)
morphic generate dump-data [table1 table2 ...] [flags]
| Flag | Type | Default | Description |
|---|---|---|---|
--name |
string | "" |
Custom migration name suffix |
--dry-run |
bool | false |
Print generated migration without writing |
--verbose |
bool | false |
Show connection and row-count details |
--conflict-key |
[]string | (auto) | Override PK detection; applied to all tables |
--where |
[]string | (none) | WHERE filter; use table:condition for per-table or just condition for all |
--json |
bool | false |
Write row data to JSONL files instead of inlining in the migration (Starlark only) |
--dsn |
string | "" |
Full database DSN (overrides host/port/etc.) |
--host |
string | localhost |
Database host |
--port |
int | (varies) | Database port |
--database |
string | "" |
Database name |
--username |
string | "" |
Database username |
--password |
string | "" |
Database password |
--sslmode |
string | disable |
SSL mode (PostgreSQL) |
| Flag | Type | Default | Description |
|---|---|---|---|
--config |
string | migrations/morphic.config.yaml |
Path to the configuration file |
- Primary-key columns are read from the migration SchemaState — the reconstructed schema built by walking your existing migration chain.
- The
--conflict-keyflag overrides automatic detection and applies the specified column(s) to all tables in the invocation. - If the table does not yet exist in the migration schema (i.e. you have not generated migrations for it), you must supply
--conflict-key.
Connection details are resolved in this order:
--dsnflag (highest priority)DATABASE_URLenvironment variable- Individual flags (
--host,--port,--database,--username,--password,--sslmode) - Config file defaults
Supported databases: PostgreSQL, MySQL, TiDB, SQLite.
By default, dump-data fetches all rows from each table. Use --where to filter which rows are included in the generated migration.
Prefix the condition with the table name and a colon:
morphic generate dump-data users orders --where "users:status='active'" --where "orders:total > 0"This fetches only active users and orders with a positive total.
Omit the table prefix to apply the condition to every table:
morphic generate dump-data users orders --where "active = 1"Multiple --where entries for the same table are combined with AND:
morphic generate dump-data users --where "users:status='active'" --where "users:created_at > '2025-01-01'"
# Equivalent to: WHERE status='active' AND created_at > '2025-01-01'The output format depends on the migration.format setting in your config file.
When format: starlark, dumping a unit_type table with two rows generates:
migration(
name = "0003_dump_unit_type",
dependencies = ["0002_add_indexes"],
operations = [
upsert_data("unit_type",
conflict_keys = ["id"],
rows = [
row(code="MET", id=1, name="Metric"),
row(code="IMP", id=2, name="Imperial"),
],
),
],
)When format: go, the same dump generates:
package main
import m "github.com/ocomsoft/morphic/migrate"
func init() {
m.Register(&m.Migration{
Name: "0003_dump_unit_type",
Dependencies: []string{"0002_add_indexes"},
Operations: []m.Operation{
&m.UpsertData{
Table: "unit_type",
ConflictKeys: []string{"id"},
Rows: []map[string]any{
{"code": "MET", "id": int64(1), "name": "Metric"},
{"code": "IMP", "id": int64(2), "name": "Imperial"},
},
},
},
})
}Each UpsertData operation translates to an INSERT ... ON CONFLICT (pk) DO UPDATE statement at migration runtime. Rollback (down) generates DELETE statements matching the conflict keys.
morphic generate dump-data countriesmorphic generate dump-data countries currencies --name seed_reference_datamorphic generate dump-data roles --dry-runmorphic generate dump-data legacy_table --conflict-key idmorphic generate dump-data countries --dsn "host=prod-db port=5432 dbname=myapp user=ro sslmode=require"morphic generate dump-data users --where "users:status='active'" --dry-runmorphic generate dump-data countries currencies --where "active = 1"morphic generate dump-data countries --verbose
# Output:
# Connecting to postgresql database...
# Fetching rows from "countries" (conflict keys: [id])...
# 42 rows fetched
# Generating migration: 0005_dump_countries
# Dependencies: [0004_add_indexes]
# Created migrations/0005_dump_countries.starThe --json flag writes row data to separate JSONL files alongside the migration instead of inlining them in the .star file. This is useful for large datasets where inline rows would make the migration file unwieldy.
morphic generate dump-data countries currencies --jsonThis creates:
migrations/
0003_dump_data.star
0003_dump_data_countries.jsonl
0003_dump_data_currencies.jsonl
The generated .star file references the JSONL files:
migration(
name = "0003_dump_data",
dependencies = ["0002_initial"],
operations = [
upsert_data(
"countries",
conflict_keys = ["code"],
rows_file = "0003_dump_data_countries.jsonl",
),
upsert_data(
"currencies",
conflict_keys = ["code"],
rows_file = "0003_dump_data_currencies.jsonl",
),
],
)Each file contains one JSON object per line, with keys sorted alphabetically. Lines starting with # are treated as comments and ignored:
# Country seed data
{"code": "AU", "name": "Australia", "population": 26000000}
{"code": "NZ", "name": "New Zealand", "population": 5000000}When morphic migrate up applies a migration with rows_file, the JSONL file is read lazily at runtime. The file must exist in the migrations directory alongside the .star file.
--jsononly works with Starlark format (the default). Using it with Go format produces an error.--dry-runwith--jsonprints the migration source and lists the JSONL files that would be created, but does not write any files.- The JSONL files are committed to version control alongside the migration.
--conflict-keyapplies to all tables in one invocation. If tables have different primary keys, run the command separately for each table.- Values are stored as plain literals (strings, ints,
None/nil). SQL quoting and escaping happen at migration runtime, not at generation time. - The
--wherecondition is appended to the query as-is. Ensure the condition is valid SQL for your target database.
- empty command — Create a blank migration for custom operations
- morphic command — Generate migrations from schema changes
- migrate command — Run
up,down,status,fakeetc. - Migrations Guide — Full guide to the Go migration framework