StoreSchema

See source code
Table 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
readonly migrations: Record<string, MigrationSequence>

sortedMigrations

readonly
readonly sortedMigrations: readonly Migration[]

types

readonly
readonly types: {
  [Record in R as Record['typeName']]: RecordType<R, any>
}

Methods

create( )

static

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

NameDescription

types

{
  [TypeName in R['typeName']]: {
    createId: any
  }
}

Object mapping type names to their RecordType definitions

options

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

NameDescription

persistedSchema

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

NameDescription

record

R

The record to migrate

persistedSchema

The schema version the record was persisted with

direction

'down' | 'up'

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

NameDescription

snapshot

The store snapshot containing data and schema information

opts

{
  mutateInputStore?: boolean
}

Options controlling migration behavior

  • mutateInputStore - Whether to modify the input store directly (default: false)

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

NameDescription

store

Store<R>

The store instance where validation is occurring

record

R

The record to validate

phase

  | 'createRecord'
  | 'initialize'
  | 'tests'
  | 'updateRecord'

The lifecycle phase where validation is happening

recordBefore

null | R

The previous version of the record (for updates)

Returns

R

The validated record, potentially modified by validation failure handler


Prev
StoreQueries
Next
StoreSideEffects