Skip to content

Seed System

Declarative data seeding with idempotent upserts, FK ordering, and stale cleanup.

Defining Seeds

typescript
import { seed, tableSeed } from '@pgbo/core/seed'

await seed(db, [
  tableSeed(tenantTable, [
    { id: 1, name: 'Acme Corp' },
    { id: 2, name: 'Other Inc' },
  ]),
  tableSeed(warehouseTable, [
    { slug: 'main', tenantId: 1, name: 'Main Warehouse' },
    { slug: 'returns', tenantId: 1, name: 'Returns Center' },
  ]),
])

Behavior

Idempotent Upserts

Seeds use INSERT ... ON CONFLICT DO UPDATE. Re-running the same seed is safe — existing records are updated, not duplicated:

typescript
// First run: inserts 2 rows
await seed(db, defs)

// Second run: updates 2 rows (no duplicates)
await seed(db, defs)

FK Ordering

Seeds are automatically sorted by foreign key dependencies using topological sort. You can define them in any order — parents are always inserted before children:

typescript
// warehouse depends on tenant via FK — pgbo seeds tenant first
await seed(db, [
  tableSeed(warehouseTable, [{ slug: 'main', tenantId: 1, name: 'Main' }]),
  tableSeed(tenantTable, [{ id: 1, name: 'Acme' }]),  // inserted first despite being listed second
])

Stale Cleanup

Remove database records not present in the seed definition:

typescript
await seed(db, [
  tableSeed(tenantTable, [
    { id: 1, name: 'Acme' },
    // id: 2 removed — will be deleted from DB
  ]),
], { cleanup: true })

Protected Records

Exclude specific records from cleanup:

typescript
await seed(db, [
  tableSeed(tenantTable, [
    { id: 1, name: 'Acme' },
  ], {
    protectedKeys: [{ id: 999 }],  // system tenant — never deleted
  }),
], { cleanup: true })

Released under the MIT License.