~ knotless Search/
↑↓ move↵ open⇧↵ all results

peterullrich.com/hatk

AT Protocol app framework

Forked from chadtmiller.com/hatk

0
0
main
127 lines · 3.6 KB Raw History
---
title: Seeds
description: Create test data for local development.
---

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

| Function                                           | Description                                                        |
| -------------------------------------------------- | ------------------------------------------------------------------ |
| `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