Examples
This page contains practical code examples demonstrating key Ankurah features.
Defining a Model
#[derive(Model, Debug, Serialize, Deserialize)]
pub struct Album {
#[active_type(YrsString)]
pub name: String,
pub artist: String,
pub year: i32,
}
This automatically generates:
AlbumView(read-only)AlbumMut(transactional updates)
See Defining Models for full documentation.
Server Setup
let storage = SledStorageEngine::with_path(storage_dir)?;
let node = Node::new_durable(Arc::new(storage), PermissiveAgent::new());
node.system.wait_loaded().await;
if node.system.root().is_none() {
node.system.create().await?;
}
let mut server = WebsocketServer::new(node);
println!("Running server...");
server.run("127.0.0.1:9797").await?;
Rust Client
let storage = SledStorageEngine::new_test()?;
let node = Node::new(Arc::new(storage), PermissiveAgent::new());
let _client = WebsocketClient::new(node.clone(), "ws://localhost:9797").await?;
node.system.wait_system_ready().await;
// Create album
let ctx = node.context(ankurah::policy::DEFAULT_CONTEXT)?;
let trx = ctx.begin();
trx.create(&Album { name: "Parade".into(), artist: "Prince".into(), year: 1986 }).await?;
trx.commit().await?;
React Component
interface Props {
albums: AlbumLiveQuery;
}
/* Bind a React observer to the component. */
const AlbumList = signalObserver(({ albums }: Props) => {
return (
<ul>
{/* Reading items registers this render as a live-query observer. */}
{albums.items.map((album) => (
<li key={album.id.to_base64()}>{album.name}</li>
))}
</ul>
);
});
See React Bindings for full documentation.
Live Query
// Using selection! macro with ctx.query()
let q: LiveQuery<AlbumView> = ctx.query(selection!("year > 1985"))?;
See Querying Data for full documentation.
Entity References
Create relationships between entities with Ref<T>:
// Create an artist
let trx = ctx.begin();
let artist = trx.create(&Artist { name: "Radiohead".into() }).await?;
let artist_id = artist.id();
trx.commit().await?;
// Create a song that references the artist
let trx = ctx.begin();
trx.create(&Song {
title: "Paranoid Android".into(),
artist: Ref::new(artist_id),
}).await?;
trx.commit().await?;
Traverse references to fetch related entities:
// Fetch the song and traverse to get the artist
let songs: Vec<SongView> = ctx.fetch("title = 'Paranoid Android'").await?;
let song = songs.first().unwrap();
// Get the referenced artist entity
let artist: ArtistView = song.artist()?.get(&ctx).await?;
println!("Artist: {}", artist.name()?);
JSON Queries
Create entities with dynamic JSON fields:
let trx = ctx.begin();
trx.create(&Track {
name: "Test Track".into(),
metadata: Json::new(serde_json::json!({
"genre": "rock",
"bpm": 120,
"tags": ["guitar", "drums"]
})),
}).await?;
trx.commit().await?;
Query by nested JSON paths:
// Query by nested JSON path
let tracks: Vec<TrackView> = ctx.fetch("metadata.genre = 'rock'").await?;
Numeric comparisons work too:
// Numeric comparison on JSON field
let fast_tracks: Vec<TrackView> = ctx.fetch("metadata.bpm > 100").await?;
Storage Backends
Postgres
let storage = Postgres::open(uri).await?;
SQLite
let storage = SqliteStorageEngine::open("ankurah.sqlite").await?;
Sled (Embedded)
let storage = SledStorageEngine::new()?;
IndexedDB (WASM)
let storage = IndexedDBStorageEngine::open("myapp").await?;
Next Steps
- Check out the Quick Start guide for step-by-step setup
- Review the Glossary to understand key terms
- Study the Architecture to see how it all fits together
- Join the Discord to discuss your use case!