Skip to content

Seeds populate your local PDS with test accounts and records during development. They live at seeds/seed.ts and use the generated seed() helper.

Minimal example

typescript
import { seed } from '$hatk'

const { createAccount, createRecord } = seed()

const alice = await createAccount('alice.test')

await createRecord(alice, 'app.bsky.actor.profile', {
  displayName: 'Alice',
  description: 'Test user',
}, { rkey: 'self' })

await createRecord(alice, 'xyz.statusphere.status', {
  status: '🚀',
  createdAt: new Date().toISOString(),
})

Seeds run automatically during hatk dev after the PDS starts. You can also run them manually:

bash
hatk seed       # run seeds/seed.ts
hatk reset      # wipe all data and re-seed from scratch

Complete seed file

A more realistic seed file creates multiple accounts, uploads images, creates follows, and staggers timestamps so time-based feeds have data to work with:

typescript
import { seed } from '$hatk'

const { createAccount, createRecord, uploadBlob } = seed()

const alice = await createAccount('alice.test')
const bob = await createAccount('bob.test')
const carol = await createAccount('carol.test')

// Stagger records over the last hour
const now = Date.now()
const ago = (minutes: number) => new Date(now - minutes * 60_000).toISOString()

// Profile with an avatar (upload returns a blob ref)
const aliceAvatar = await uploadBlob(alice, './seeds/images/alice.png')
await createRecord(
  alice,
  'app.bsky.actor.profile',
  {
    displayName: 'Alice',
    description: 'Indie and alt-pop listener',
    avatar: aliceAvatar,
  },
  { rkey: 'self' },
)

// Follows — Alice follows Bob and Carol
await createRecord(
  alice,
  'app.bsky.graph.follow',
  { subject: bob.did, createdAt: new Date().toISOString() },
  { rkey: 'bob' },
)
await createRecord(
  alice,
  'app.bsky.graph.follow',
  { subject: carol.did, createdAt: new Date().toISOString() },
  { rkey: 'carol' },
)

// App records with staggered timestamps
await createRecord(
  alice,
  'fm.teal.alpha.feed.play',
  {
    trackName: 'Blinding Lights',
    artists: [{ artistName: 'The Weeknd' }],
    releaseName: 'After Hours',
    playedTime: ago(50),
  },
  { rkey: 'blinding-lights' },
)

await createRecord(
  bob,
  'fm.teal.alpha.feed.play',
  {
    trackName: 'HUMBLE.',
    artists: [{ artistName: 'Kendrick Lamar' }],
    releaseName: 'DAMN.',
    playedTime: ago(30),
  },
  { rkey: 'humble' },
)

console.log('\n[seed] Done!')

seed() helpers

FunctionDescription
createAccount(handle)Create a test account on the local PDS. Returns { did, handle }
createRecord(account, collection, record, opts?)Create a record. Pass { rkey } in opts for a specific record key
uploadBlob(account, filePath)Upload a file and return a blob reference for use in records

Records are validated against your project's lexicons before being written, so you get errors at seed time if the data doesn't match your schema.

Tips

  • Use { rkey: 'self' } for singleton records like profiles
  • Place test images in seeds/images/
  • Use the ago helper pattern to spread records across time for testing feeds and cursors
  • createAccount reuses an existing account if the handle already exists, so re-running seeds is safe