ObjectStackObjectStack

Seed Loader schema — Data Protocol reference

Defines the schemas for metadata-driven seed data loading with automatic relationship resolution, dependency ordering, and multi-pass insertion.

Seed Loader Protocol

Defines the schemas for metadata-driven seed data loading with automatic relationship resolution, dependency ordering, and multi-pass insertion.

Architecture Alignment

  • Salesforce Data Loader: External ID-based upsert with relationship resolution
  • ServiceNow: Sys ID and display value mapping during import
  • Airtable: Linked record resolution via display names

Loading Flow

1. Build object dependency graph from field metadata (lookup/master_detail)
2. Topological sort → determine insert order (parents before children)
3. Pass 1: Insert/upsert records, resolve references via externalId
4. Pass 2: Fill deferred references (circular/delayed dependencies)
5. Validate & report unresolved references
6. Return structured result with per-object stats

Source: packages/spec/src/data/seed-loader.zod.ts

TypeScript Usage

import { ObjectDependencyGraphSchema, ObjectDependencyNodeSchema, ReferenceResolutionSchema, ReferenceResolutionErrorSchema, SeedIdentitySchema, SeedLoadResultSchema, SeedLoaderConfigSchema, SeedLoaderRequestSchema, SeedLoaderResultSchema } from '@objectstack/spec/data';
import type { ObjectDependencyGraph, ObjectDependencyNode, ReferenceResolution, ReferenceResolutionError, SeedIdentity, SeedLoadResult, SeedLoaderConfig, SeedLoaderRequest, SeedLoaderResult } from '@objectstack/spec/data';

// Validate data
const result = ObjectDependencyGraphSchema.parse(data);

ObjectDependencyGraph

Complete object dependency graph for seed data loading

Properties

PropertyTypeRequiredDescription
nodes{ object: string; dependsOn: string[]; references: object[] }[]✅All objects in the dependency graph
insertOrderstring[]✅Topologically sorted insert order
circularDependenciesstring[][]optional (default: [])Circular dependency chains (e.g., [["a", "b", "a"]])

Nested Shape: ObjectDependencyGraph.nodes[number]

Object node in the seed data dependency graph

PropertyTypeRequiredDescription
objectstring✅Object name (snake_case)
dependsOnstring[]✅Objects this object depends on
references{ field: string; targetObject: string; targetField?: string; fieldType: Enum<'lookup' | 'master_detail' | 'user'>; … }[]✅Field-level reference details

ObjectDependencyNode

Object node in the seed data dependency graph

Properties

PropertyTypeRequiredDescription
objectstring✅Object name (snake_case)
dependsOnstring[]✅Objects this object depends on
references{ field: string; targetObject: string; targetField?: string; fieldType: Enum<'lookup' | 'master_detail' | 'user'>; … }[]✅Field-level reference details

Nested Shape: ObjectDependencyNode.references[number]

Describes how a field reference is resolved during seed loading

PropertyTypeRequiredDescription
fieldstring✅Source field name containing the reference value
targetObjectstring✅Target object name (snake_case)
targetFieldstringoptional (default: "name")Field on target object used for matching
fieldTypeEnum<'lookup' | 'master_detail' | 'user'>✅Relationship field type
multiplebooleanoptionalField stores an array of references (multiple: true)

ReferenceResolution

Describes how a field reference is resolved during seed loading

Properties

PropertyTypeRequiredDescription
fieldstring✅Source field name containing the reference value
targetObjectstring✅Target object name (snake_case)
targetFieldstringoptional (default: "name")Field on target object used for matching
fieldTypeEnum<'lookup' | 'master_detail' | 'user'>✅Relationship field type
multiplebooleanoptionalField stores an array of references (multiple: true)

ReferenceResolutionError

Actionable error for a failed reference resolution

Properties

PropertyTypeRequiredDescription
sourceObjectstring✅Object with the broken reference
fieldstring✅Field name with unresolved reference
targetObjectstring✅Target object searched for the reference
targetFieldstring✅ExternalId field used for matching
attemptedValueany✅Value that failed to resolve
recordIndexinteger✅Index of the record in the dataset
messagestring✅Human-readable error description

SeedIdentity

Identity context for resolving os.user / os.org in seed CEL values

Properties

PropertyTypeRequiredDescription
user{ id: string; role?: string; email?: string }optionalSubject bound to os.user in seed CEL expressions
org{ id: string; tier?: string }optionalOrganization bound to os.org in seed CEL expressions

SeedLoadResult

Result of loading a single dataset

Properties

PropertyTypeRequiredDescription
objectstring✅Object that was loaded
modeEnum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>✅Import mode used
insertedinteger✅Records inserted
updatedinteger✅Records updated
skippedinteger✅Records skipped
erroredinteger✅Records with errors
totalinteger✅Total records in dataset
referencesResolvedinteger✅References resolved via externalId
referencesDeferredinteger✅References deferred to second pass
referencesDroppedintegeroptional (default: 0)Reference fields dropped from records that were still written
summariesStaleintegeroptional (default: 0)Roll-up summary values left stale by writes for this dataset
landedIdsstring[]optional (default: [])Ids of the stored rows this dataset landed on: inserted, updated, or matched on replay
errors{ sourceObject: string; field: string; targetObject: string; targetField: string; … }[]optional (default: [])Reference resolution errors

Nested Shape: SeedLoadResult.errors[number]

Actionable error for a failed reference resolution

PropertyTypeRequiredDescription
sourceObjectstring✅Object with the broken reference
fieldstring✅Field name with unresolved reference
targetObjectstring✅Target object searched for the reference
targetFieldstring✅ExternalId field used for matching
attemptedValueany✅Value that failed to resolve
recordIndexinteger✅Index of the record in the dataset
messagestring✅Human-readable error description

SeedLoaderConfig

Seed data loader configuration

Properties

PropertyTypeRequiredDescription
dryRunbooleanoptional (default: false)Validate references without writing data
haltOnErrorbooleanoptional (default: false)Stop on first reference resolution error
multiPassbooleanoptional (default: true)Enable multi-pass loading for circular dependencies
defaultModeEnum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>optional (default: "upsert")Default conflict resolution strategy
batchSizeintegeroptional (default: 1000)Maximum records per batch insert/upsert
transactionbooleanoptional (default: false)Wrap entire load in a transaction (all-or-nothing)
envEnum<'prod' | 'dev' | 'test'>optionalOnly load datasets matching this environment
localestringoptionalOnly load datasets scoped to this locale (BCP-47 tag)
organizationIdstringoptionalTarget organization id for per-tenant seed replay
identity{ user?: object; org?: object }optionalIdentity bound to os.user / os.org when resolving CEL seed values

Nested Shape: SeedLoaderConfig.identity

PropertyTypeRequiredDescription
user{ id: string; role?: string; email?: string }optionalSubject bound to os.user in seed CEL expressions
org{ id: string; tier?: string }optionalOrganization bound to os.org in seed CEL expressions

SeedLoaderRequest

Seed loader request with datasets and configuration

Properties

PropertyTypeRequiredDescription
seeds{ object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[]✅Seeds to load
config{ dryRun?: boolean; haltOnError?: boolean; multiPass?: boolean; defaultMode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; … }optional (has default)Loader configuration

Nested Shape: SeedLoaderRequest.seeds[number]

PropertyTypeRequiredDescription
objectstring✅Target Object Name
externalIdstring | string[]optional (default: "name")Field (or composite list of fields) matched for the uniqueness check
modeEnum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>optional (default: "upsert")Conflict resolution strategy
envEnum<'prod' | 'dev' | 'test'>[]optional (default: ["prod","dev","test"])Applicable environments
localestring[]optionalApplicable locales (BCP-47 tags); omitted applies to every locale. The publish and install paths do not filter by locale — they load every dataset and warn
recordsRecord<string, any>[]✅Data records
_lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>optionalItem-level lock — controls overlay & delete (ADR-0010).
_lockReasonstringoptionalHuman-readable reason shown when a write is refused by _lock.
_lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalLayer that set _lock (artifact | package | env-forced).
_provenanceEnum<'package' | 'org' | 'env-forced'>optionalOrigin of the item (package | org | env-forced).
_packageIdstringoptionalOwning package machine id.
_packageVersionstringoptionalOwning package version.
_lockDocsUrlstringoptionalOptional documentation link surfaced next to _lockReason.

Nested Shape: SeedLoaderRequest.config

PropertyTypeRequiredDescription
dryRunbooleanoptional (default: false)Validate references without writing data
haltOnErrorbooleanoptional (default: false)Stop on first reference resolution error
multiPassbooleanoptional (default: true)Enable multi-pass loading for circular dependencies
defaultModeEnum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>optional (default: "upsert")Default conflict resolution strategy
batchSizeintegeroptional (default: 1000)Maximum records per batch insert/upsert
transactionbooleanoptional (default: false)Wrap entire load in a transaction (all-or-nothing)
envEnum<'prod' | 'dev' | 'test'>optionalOnly load datasets matching this environment
localestringoptionalOnly load datasets scoped to this locale (BCP-47 tag)
organizationIdstringoptionalTarget organization id for per-tenant seed replay
identity{ user?: object; org?: object }optionalIdentity bound to os.user / os.org when resolving CEL seed values

SeedLoaderResult

Complete seed loader result

Properties

PropertyTypeRequiredDescription
successboolean✅Overall success status
dryRunboolean✅Whether this was a dry-run
dependencyGraph{ nodes: object[]; insertOrder: string[]; circularDependencies?: string[][] }✅Object dependency graph
results{ object: string; mode: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; inserted: integer; updated: integer; … }[]✅Per-object load results
errors{ sourceObject: string; field: string; targetObject: string; targetField: string; … }[]✅All reference resolution errors
summary{ objectsProcessed: integer; totalRecords: integer; totalInserted: integer; totalUpdated: integer; … }✅Summary statistics

Nested Shape: SeedLoaderResult.dependencyGraph

PropertyTypeRequiredDescription
nodes{ object: string; dependsOn: string[]; references: object[] }[]✅All objects in the dependency graph
insertOrderstring[]✅Topologically sorted insert order
circularDependenciesstring[][]optional (default: [])Circular dependency chains (e.g., [["a", "b", "a"]])

Nested Shape: SeedLoaderResult.results[number]

Result of loading a single dataset

PropertyTypeRequiredDescription
objectstring✅Object that was loaded
modeEnum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>✅Import mode used
insertedinteger✅Records inserted
updatedinteger✅Records updated
skippedinteger✅Records skipped
erroredinteger✅Records with errors
totalinteger✅Total records in dataset
referencesResolvedinteger✅References resolved via externalId
referencesDeferredinteger✅References deferred to second pass
referencesDroppedintegeroptional (default: 0)Reference fields dropped from records that were still written
summariesStaleintegeroptional (default: 0)Roll-up summary values left stale by writes for this dataset
landedIdsstring[]optional (default: [])Ids of the stored rows this dataset landed on: inserted, updated, or matched on replay
errors{ sourceObject: string; field: string; targetObject: string; targetField: string; … }[]optional (default: [])Reference resolution errors

Nested Shape: SeedLoaderResult.errors[number]

Actionable error for a failed reference resolution

PropertyTypeRequiredDescription
sourceObjectstring✅Object with the broken reference
fieldstring✅Field name with unresolved reference
targetObjectstring✅Target object searched for the reference
targetFieldstring✅ExternalId field used for matching
attemptedValueany✅Value that failed to resolve
recordIndexinteger✅Index of the record in the dataset
messagestring✅Human-readable error description

Nested Shape: SeedLoaderResult.summary

PropertyTypeRequiredDescription
objectsProcessedinteger✅Total objects processed
totalRecordsinteger✅Total records across all objects
totalInsertedinteger✅Total records inserted
totalUpdatedinteger✅Total records updated
totalSkippedinteger✅Total records skipped
totalErroredinteger✅Total records with errors
totalReferencesResolvedinteger✅Total references resolved
totalReferencesDeferredinteger✅Total references deferred
totalReferencesDroppedintegeroptional (default: 0)Total reference fields dropped from written records
totalSummariesStaleintegeroptional (default: 0)Total roll-up summary values left stale across the load
circularDependencyCountinteger✅Circular dependency chains detected
durationMsnumber✅Load duration in milliseconds

On this page