Seed Data Guide
How to create, manage, and validate seed data in HotCRM packages.
Seed Data Guide
HotCRM uses seed data to populate the database with initial reference data and sample records for development, testing, and demo environments. Each package can define its own seed files that are loaded in dependency order.
Overview
| Metric | Value |
|---|---|
| Total Seed Files | 39 across 14 packages |
| Total Dataset Files | 43 across 14 packages |
| Seed File Convention | *.seed.ts (raw data arrays) |
| Dataset File Convention | *.dataset.ts (DatasetSchema-compliant) |
| Loader Script | scripts/seed.ts |
| Load Strategy | Dependency-ordered (topological sort) |
DatasetSchema Protocol
All seed data is wrapped in DatasetSchema-compliant dataset files per the @objectstack/spec Data Seeding Protocol. Each dataset declares its target object, upsert key, conflict mode, and environment scope.
Dataset File Format
// packages/{package}/src/{entity}.dataset.ts
import { type DatasetInput, DatasetSchema } from '@objectstack/spec/data';
import { EntitySeedData } from './entity.seed.js';
export const EntityDataset: DatasetInput = {
object: 'entity', // Target object (snake_case)
externalId: 'name', // Idempotent upsert key
mode: 'upsert', // Conflict strategy
env: ['prod', 'dev', 'test'], // Environment scope
records: EntitySeedData, // Data records from *.seed.ts
};
DatasetSchema.parse(EntityDataset);
export default EntityDataset;DatasetSchema Fields
| Field | Type | Default | Description |
|---|---|---|---|
object | string | (required) | Target object name (snake_case) |
externalId | string | 'name' | Field used as upsert key for idempotent loading |
mode | 'upsert' | 'insert' | 'ignore' | 'replace' | 'update' | 'upsert' | Conflict resolution strategy |
env | ('prod' | 'dev' | 'test')[] | ['prod', 'dev', 'test'] | Which environments to load this data in |
records | Record<string, unknown>[] | (required) | Array of data records |
Environment & Mode Strategy
| Data Type | Mode | Environment | Example |
|---|---|---|---|
| Reference data (currencies, countries, industries) | upsert | ['prod', 'dev', 'test'] | currency.dataset.ts |
| Demo/sample business data (accounts, leads) | ignore | ['dev', 'test'] | account.dataset.ts |
- Reference data uses
upsertto ensure it's always current, in all environments. - Demo data uses
ignoreto avoid overwriting production data, and is excluded fromprod.
File Naming Convention
Seed files and dataset files follow the standard HotCRM naming convention:
packages/{package}/src/{entity}.seed.ts ← Raw data arrays
packages/{package}/src/{entity}.dataset.ts ← DatasetSchema wrapperExamples:
packages/crm/src/account.seed.ts— Account sample data (raw array)packages/crm/src/account.dataset.ts— Account dataset (DatasetSchema-compliant)packages/core/src/system.seed.ts— System-level reference datapackages/core/src/currency.dataset.ts— Currency dataset
Writing Seed & Dataset Files
Step 1: Create the Seed File (Raw Data)
Each seed file exports a named array of plain objects representing database records:
/**
* Account Seed Data
* Sample accounts representing a mix of industries and sizes
*/
export const AccountSeedData = [
{
name: 'Acme Corporation',
industry: 'Technology',
type: 'Customer',
annual_revenue: 5000000,
employees: 250,
status: 'active',
},
{
name: 'GlobalTech Solutions',
industry: 'Technology',
type: 'Customer',
annual_revenue: 12000000,
employees: 800,
status: 'active',
},
];
export default AccountSeedData;Step 2: Create the Dataset File (DatasetSchema Wrapper)
Wrap the seed data in a DatasetSchema-compliant dataset:
import { type DatasetInput, DatasetSchema } from '@objectstack/spec/data';
import { AccountSeedData } from './account.seed.js';
export const AccountDataset: DatasetInput = {
object: 'account',
externalId: 'name',
mode: 'ignore', // Demo data — don't overwrite
env: ['dev', 'test'], // Not loaded in production
records: AccountSeedData,
};
DatasetSchema.parse(AccountDataset);
export default AccountDataset;Step 3: Register in Plugin
Add the dataset to the plugin's data field:
import { AccountDataset } from './account.dataset.js';
export const CRMPlugin = {
// ...other fields...
data: [
AccountDataset,
// ...other datasets...
],
};Best Practices
- Export both named and default: Always export both
export const EntitySeedDataandexport default. - Include realistic data: Use realistic names, amounts, and dates for demo environments.
- Cover edge cases: Include records in different statuses (active, inactive, draft, etc.).
- Use consistent types: Match field types to the corresponding
*.object.tsdefinition. - Minimum 3-5 records: Each seed file should have enough data for meaningful testing.
Package Load Order
Seed data is loaded in dependency order to ensure referential integrity. The load order is defined in scripts/seed.ts:
| Order | Package | Dependencies |
|---|---|---|
| 1 | core | — |
| 2 | crm | core |
| 3 | finance | core, crm |
| 4 | products | core |
| 5 | hr | core |
| 6 | support | core, crm |
| 7 | marketing | core, crm |
| 8 | analytics | core |
| 9 | integration | core |
| 10 | community | core |
| 11 | healthcare | core, crm |
| 12 | financial-services | core, crm, finance |
| 13 | real-estate | core, crm |
| 14 | education | core |
Seed Files by Package
| Package | Seed Files |
|---|---|
| Core | system.seed.ts |
| CRM | account, contact, lead, opportunity, industry |
| Finance | currency, invoice, payment |
| Products | product, price_book |
| HR | employee, department, job_posting |
| Support | case, kb_article |
| Marketing | campaign, campaign_type, email_template |
| Analytics | report |
| Integration | connection |
| Community | idea, topic |
| Healthcare | patient, appointment, insurance, prescription |
| Financial Services | wealth_account, portfolio, advisory, kyc |
| Real Estate | property, listing, showing, real_estate_offer |
| Education | course, student, enrollment, scholarship |
Validation
HotCRM provides a seed data validation utility in packages/core/src/seed_validation.ts:
import { validateSeedData } from '@hotcrm/core/seed_validation';
const result = validateSeedData(AccountSeedData, {
minRecords: 5,
requiredFields: ['name', 'industry', 'status'],
strictRequired: true,
});
console.log(result.valid); // true
console.log(result.recordCount); // 10
console.log(result.errors); // []Running the Seed Loader
# Load all seed data for dev environment (default)
npx ts-node scripts/seed.ts
# Load seed data for production (only reference data)
npx ts-node scripts/seed.ts --prod
# Load seed data for test environment
npx ts-node scripts/seed.ts --test
# Reset and reload all seed data
npx ts-node scripts/seed.ts --resetAdding Seed Data to a New Package
- Create the seed file:
packages/{pkg}/src/{entity}.seed.ts - Create the dataset file:
packages/{pkg}/src/{entity}.dataset.ts - Register the dataset in the plugin's
datafield inpackages/{pkg}/src/plugin.ts - Add the package to
packages/core/src/dependency_graph.ts - Write validation tests in
packages/core/__tests__/unit/seeds/
Related Resources
- Metadata Types Guide — All supported metadata types
- Product Roadmap — Development milestones
- Contributing Guide