Skip to content
Strata Sync

AI agents: fetch the documentation index at llms.txt. Markdown versions are available by appending .md to any page URL, including this page's markdown.

Decorators

Decorator reference for defining synced models, properties, and relations in Strata Sync.

Register metadata with the ModelRegistry at class definition time. The sync engine uses this for change tracking, serialization, delta processing, and schema hashing.

Enable decorators in tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

Common options

Shared by several decorators. Individual sections reference this table.

OptionTypeDefaultDescription
lazybooleanfalseDefers hydration until first access.
serializerPropertySerializer-Custom serializer for converting values to and from their stored representation. See Custom serializers.

@ClientModel

Registers a synced model.

import { Model, ClientModel } from "@stratasync/core";

@ClientModel("Task")
class Task extends Model {
  // properties...
}

Signature: ClientModel(modelName: string, options?: ModelOptions)

ModelOptions

OptionTypeDefaultDescription
loadStrategyLoadStrategy"instant"How and when the model data is fetched.
partialLoadModePartialLoadMode-Hydration priority for partial models.
usedForPartialIndexesboolean-Whether this model is used for partial index dependencies.
schemaVersionnumber-Schema version for migration tracking.
tableNamestring-Database table name override.
groupKeystring-Sync-group key field for multi-tenancy.

Load strategies

@ClientModel("Project", { loadStrategy: "instant" })
class Project extends Model {
  /* ... */
}

@ClientModel("Attachment", { loadStrategy: "lazy" })
class Attachment extends Model {
  /* ... */
}

@ClientModel("Comment", { loadStrategy: "partial" })
class Comment extends Model {
  /* ... */
}
StrategyBehavior
"instant"Loaded during bootstrap. Always available in memory.
"lazy"Loaded on first access, cached thereafter.
"partial"Loaded on demand, partially hydrated.
"explicitlyRequested"Never auto-loaded. Must be explicitly requested via ensureModel.
"local"Never synced. Only stored locally on the device.

@Property

Marks a synced property for change tracking and delta processing.

import { Model, ClientModel, Property } from "@stratasync/core";

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @Property()
  status = "todo";

  @Property({ lazy: true })
  description = "";

  @Property({ serializer: dateSerializer })
  dueDate: Date | null = null;
}

Signature: Property(options?: PropertyOptions)

PropertyOptions

Supports common options (lazy, serializer). No additional options.

Custom serializers

Handle types that need conversion for storage:

import type { PropertySerializer } from "@stratasync/core";

const dateSerializer: PropertySerializer<Date | null> = {
  serialize(value: Date | null): unknown {
    return value ? value.toISOString() : null;
  },
  deserialize(value: unknown): Date | null {
    return typeof value === "string" ? new Date(value) : null;
  },
};

@ClientModel("Event")
class Event extends Model {
  @Property({ serializer: dateSerializer })
  startsAt: Date | null = null;
}

@EphemeralProperty

Observable but not persisted to the server. Use for reactive local UI state.

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @EphemeralProperty()
  isExpanded = false;

  @EphemeralProperty()
  localDraft = "";
}

Signature: EphemeralProperty(options?: PropertyOptions)

Same common options as @Property. Participates in MobX observability but excluded from transactions and deltas.

@Reference

Creates a foreign-key reference to another model with a lazy-resolved accessor.

@ClientModel("Task")
class Task extends Model {
  @Reference(() => Project, "tasks")
  project!: Project;
  // Also creates: projectId: string (the foreign key)
}

Signature: Reference(modelFactory: () => ModelConstructor, inverseProperty?: string, options?: ReferenceOptions)

ParameterDescription
modelFactoryFactory function returning the referenced model constructor. Uses a factory to avoid circular deps.
inversePropertyName of the inverse collection property on the referenced model (for example, "tasks" on Project).

ReferenceOptions

Supports common options (lazy, serializer), plus:

OptionTypeDefaultDescription
foreignKeystring"<propertyName>Id"Override the foreign key field name.
nullableboolean-Whether the reference can be null.
indexedbooleantrue (when inverseProperty is set)Whether to index this foreign key.

Custom foreign key

@ClientModel("Task")
class Task extends Model {
  @Reference(() => User, undefined, { foreignKey: "ownerId" })
  assignee!: User;
  // Creates: ownerId: string (instead of default "assigneeId")
}

@ManyToOne

Alias for @Reference. Accepts a model name string or factory function.

@ClientModel("Task")
class Task extends Model {
  @ManyToOne("Project", "tasks")
  project!: Project;

  @ManyToOne(() => User)
  assignee!: User;
}

Signature: ManyToOne(modelNameOrFactory: string | (() => ModelConstructor), inverseProperty?: string, options?: ReferenceOptions)

Options are the same as @Reference.

@OneToMany

Defines the parent side of a one-to-many relationship. Returns a LazyCollection of child models.

@ClientModel("Project")
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}

Signature: OneToMany(options?: ReferenceCollectionOptions)

ReferenceCollectionOptions

Supports common options (lazy, serializer), plus:

OptionTypeDefaultDescription
foreignKeystring-Override the foreign key used for the collection lookup.
indexedboolean-Whether the collection uses an index.
nullableboolean-Whether the collection can be null.

Inverse side pairing

@ManyToOne on the child creates the foreign key; @OneToMany on the parent creates the collection.

@ClientModel("Project")
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @ManyToOne("Project", "tasks")
  project!: Project;
  // Creates projectId foreign key, which @OneToMany uses
}

@BackReference

Defines an inverse lookup for relationships declared on the other side.

@ClientModel("Comment")
class Comment extends Model {
  @Property()
  body = "";

  @BackReference({ foreignKey: "commentId" })
  replies!: Reply[];
}

Signature: BackReference(options?: BackReferenceOptions)

BackReferenceOptions

Supports common options (lazy, serializer), plus:

OptionTypeDefaultDescription
foreignKeystring-The foreign key field on the related model.

@ReferenceArray

An ordered array of references, typically for many-to-many relationships through a join model.

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @ReferenceArray({ through: "TaskTag" })
  tags!: Tag[];
}

Signature: ReferenceArray(options?: ReferenceArrayOptions)

ReferenceArrayOptions

Supports common options (lazy, serializer), plus:

OptionTypeDefaultDescription
throughstring-Join/through model name for many-to-many relationships.

@ReferenceCollection

Alias for @OneToMany. Use whichever name reads better in your domain model. See @OneToMany for options and usage.

Complete example

Every decorator in a minimal project management domain:

import {
  Model,
  ClientModel,
  Property,
  EphemeralProperty,
  ManyToOne,
  OneToMany,
  ReferenceArray,
} from "@stratasync/core";
import type { LazyCollection } from "@stratasync/core";

@ClientModel("Project", { loadStrategy: "instant" })
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}

@ClientModel("Task", { loadStrategy: "instant" })
class Task extends Model {
  @Property()
  title = "";

  @Property({ serializer: dateSerializer })
  dueDate: Date | null = null;

  @ManyToOne("Project", "tasks")
  project!: Project;

  @ReferenceArray({ through: "TaskTag" })
  tags!: Tag[];

  @EphemeralProperty()
  isSelected = false;
}