StoreSchema
See source codeTable of contents
Manages the schema definition, validation, and migration system for a Store.
StoreSchema coordinates record types, handles data migrations between schema versions, validates records, and provides the foundational structure for reactive stores. It acts as the central authority for data consistency and evolution within the store system.
class StoreSchema<R extends UnknownRecord, P = unknown> {}
Example
// Define record types
const Book = createRecordType<Book>('book', { scope: 'document' })
const Author = createRecordType<Author>('author', { scope: 'document' })
// Create schema with migrations
const schema = StoreSchema.create(
{ book: Book, author: Author },
{
migrations: [bookMigrations, authorMigrations],
onValidationFailure: (failure) => {
console.warn('Validation failed, using default:', failure.error)
return failure.record // or return a corrected version
},
}
)
// Use with store
const store = new Store({ schema })
Properties
migrations
readonly migrations: Record<string, MigrationSequence>
sortedMigrations
readonly sortedMigrations: readonly Migration[]
types
readonly types: {
[Record in R as Record['typeName']]: RecordType<R, any>
}
Methods
create( )
Creates a new StoreSchema with the given record types and options.
This static factory method is the recommended way to create a StoreSchema. It ensures type safety while providing a clean API for schema definition.
static create<R extends UnknownRecord, P = unknown>(
types: {
[TypeName in R['typeName']]: {
createId: any
}
},
options?: StoreSchemaOptions<R, P>
): StoreSchema<R, P>
Example
const Book = createRecordType<Book>('book', { scope: 'document' })
const Author = createRecordType<Author>('author', { scope: 'document' })
const schema = StoreSchema.create(
{
book: Book,
author: Author,
},
{
migrations: [bookMigrations],
onValidationFailure: (failure) => failure.record,
}
)
Parameters
Name | Description |
---|---|
|
Object mapping type names to their RecordType definitions |
|
Optional configuration for migrations, validation, and integrity checking |
Returns
StoreSchema<R, P>
A new StoreSchema instance
getMigrationsSince( )
Gets all migrations that need to be applied to upgrade from a persisted schema to the current schema version.
This method compares the persisted schema with the current schema and determines which migrations need to be applied to bring the data up to date. It handles both regular migrations and retroactive migrations, and caches results for performance.
getMigrationsSince(
persistedSchema: SerializedSchema
): Result<Migration[], string>
Example
const persistedSchema = {
schemaVersion: 2,
sequences: { 'com.tldraw.book': 1, 'com.tldraw.author': 0 },
}
const migrationsResult = schema.getMigrationsSince(persistedSchema)
if (migrationsResult.ok) {
console.log('Migrations to apply:', migrationsResult.value.length)
// Apply each migration to bring data up to date
}
Parameters
Name | Description |
---|---|
| The schema version that was previously persisted |
Returns
Result<Migration[], string>
A Result containing the list of migrations to apply, or an error message
migratePersistedRecord( )
Migrates a single persisted record to match the current schema version.
This method applies the necessary migrations to transform a record from an older (or newer) schema version to the current version. It supports both forward ('up') and backward ('down') migrations.
migratePersistedRecord(
record: R,
persistedSchema: SerializedSchema,
direction?: 'down' | 'up'
): MigrationResult<R>
Example
const oldRecord = {
id: 'book:1',
typeName: 'book',
title: 'Old Title',
publishDate: '2020-01-01',
}
const oldSchema = { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } }
const result = schema.migratePersistedRecord(oldRecord, oldSchema, 'up')
if (result.type === 'success') {
console.log('Migrated record:', result.value)
// Record now has publishedYear instead of publishDate
} else {
console.error('Migration failed:', result.reason)
}
Parameters
Name | Description |
---|---|
|
The record to migrate |
| The schema version the record was persisted with |
|
Direction to migrate ('up' for newer, 'down' for older) |
Returns
A MigrationResult containing the migrated record or an error
migrateStoreSnapshot( )
Migrates an entire store snapshot to match the current schema version.
This method applies all necessary migrations to bring a persisted store snapshot up to the current schema version. It handles both record-level and store-level migrations, and can optionally mutate the input store for performance.
migrateStoreSnapshot(
snapshot: StoreSnapshot<R>,
opts?: {
mutateInputStore?: boolean
}
): MigrationResult<SerializedStore<R>>
Example
const snapshot = {
schema: { schemaVersion: 2, sequences: { 'com.tldraw.book': 1 } },
store: {
'book:1': {
id: 'book:1',
typeName: 'book',
title: 'Old Book',
publishDate: '2020-01-01',
},
},
}
const result = schema.migrateStoreSnapshot(snapshot)
if (result.type === 'success') {
console.log('Migrated store:', result.value)
// All records are now at current schema version
}
Parameters
Name | Description |
---|---|
| The store snapshot containing data and schema information |
|
Options controlling migration behavior
|
Returns
A MigrationResult containing the migrated store or an error
serialize( )
Serializes the current schema to a SerializedSchemaV2 format.
This method creates a serialized representation of the current schema, capturing the latest version number for each migration sequence. The result can be persisted and later used to determine what migrations need to be applied when loading data.
serialize(): SerializedSchemaV2
Example
const serialized = schema.serialize()
console.log(serialized)
// {
// schemaVersion: 2,
// sequences: {
// 'com.tldraw.book': 3,
// 'com.tldraw.author': 2
// }
// }
// Store this with your data for future migrations
localStorage.setItem('schema', JSON.stringify(serialized))
validateRecord( )
Validates a record using its corresponding RecordType validator.
This method ensures that records conform to their type definitions before being stored. If validation fails and an onValidationFailure handler is provided, it will be called to potentially recover from the error.
validateRecord(
store: Store<R>,
record: R,
phase: 'createRecord' | 'initialize' | 'tests' | 'updateRecord',
recordBefore: null | R
): R
Example
try {
const validatedBook = schema.validateRecord(
store,
{ id: 'book:1', typeName: 'book', title: '', author: 'Jane Doe' },
'createRecord',
null
)
} catch (error) {
console.error('Record validation failed:', error)
}
Parameters
Name | Description |
---|---|
|
The store instance where validation is occurring |
|
The record to validate |
|
The lifecycle phase where validation is happening |
|
The previous version of the record (for updates) |
Returns
R
The validated record, potentially modified by validation failure handler