Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Defining Models

Models define the structure of your entities. Define them once in Rust, and they work everywhere—native servers, browser clients, and mobile apps.

Basic Model Definition

Use the #[derive(Model)] macro to define a model:

#[derive(Model, Debug, Serialize, Deserialize)]
pub struct Album {
    #[active_type(YrsString)]
    pub name: String,
    pub artist: String,
    pub year: i32,
}

This single definition generates:

Generated TypePurpose
AlbumThe model struct for creating new entities
AlbumViewRead-only view of an entity’s current state
AlbumMutMutable handle for updating entities in a transaction

Field Types

Basic Types

#[derive(Model, Debug, Serialize, Deserialize)]
pub struct Task {
    pub title: String,
    pub completed: bool,
    pub priority: i32,
}

Supported types include:

  • String
  • bool
  • Integers: i8, i16, i32, i64, u8, u16, u32, u64
  • f32, f64

CRDT Types

Use #[active_type(...)] to specify a CRDT backend for a field. The first supported CRDT type is YrsString for collaborative text:

#[derive(Model, Debug, Serialize, Deserialize)]
pub struct Document {
    #[active_type(YrsString)]
    pub content: String,
    pub title: String,
}

Entity References

Use Ref<T> to create typed references between entities:

#[derive(Model, Debug, Serialize, Deserialize, Clone)]
pub struct Artist {
    pub name: String,
}

#[derive(Model, Debug, Serialize, Deserialize, Clone)]
pub struct Song {
    pub title: String,
    pub artist: Ref<Artist>,
}

References enable graph-style navigation between related entities.

JSON Fields

Use Json for schemaless, dynamic data:

#[derive(Model, Debug, Serialize, Deserialize, Clone)]
pub struct Track {
    pub name: String,
    pub metadata: Json,
}

JSON fields support nested path queries like metadata.genre = 'rock'.

Creating Entities

Use a transaction to create new entities:

let trx = ctx.begin();

let album = trx.create(&Album {
    name: "Parade".into(),
    artist: "Prince".into(),
    year: 1986,
}).await?;

let album_id = album.id();
trx.commit().await?;

Reading Entities

Access data through the View type:

let view: AlbumView = ctx.get(album_id).await?;
println!("Album: {} by {} ({})", view.name()?, view.artist()?, view.year()?);

Generated TypeScript

When you build your WASM bindings, TypeScript types are generated automatically:

// Generated from your Rust model
interface AlbumView {
  id: EntityId;
  name: string;
  artist: string;
  year: number;
}

// Static methods on the model class
class Album {
  static query(ctx: Context, query: string): AlbumLiveQuery;
  static create(trx: Transaction, data: AlbumData): Promise<AlbumView>;
}

Next Steps