Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mainser/cindel/llms.txt

Use this file to discover all available pages before exploring further.

Cindel models relationships between collections using CindelLink<T> for to-one relations and CindelLinks<T> for to-many relations. These link fields are declared directly on your model classes and are managed by the generated schema. A third annotation, @Backlink, marks a field as the read-only inverse of a forward link so you can traverse a relationship from either end without storing redundant data. Add a CindelLink<T> field to hold a reference to a single related object, or a CindelLinks<T> field to hold references to a set of related objects. Both types start unloaded — the related objects are not fetched automatically when the owner is read from storage:
@Collection(name: 'artists')
class Artist {
  Id dbId = autoIncrement;
  late String name;

  @Backlink(to: 'featuredArtists')
  final songs = CindelLinks<Song>();
}

@Collection(name: 'songs')
class Song {
  Id dbId = autoIncrement;
  late String title;

  final featuredArtists = CindelLinks<Artist>();
  final primaryArtist = CindelLink<Artist>();
}
Song.featuredArtists is the forward CindelLinks<Artist> relation — it stores the artist ids. Song.primaryArtist is the forward CindelLink<Artist> relation. Artist.songs is the backlink — it declares to: 'featuredArtists', meaning it reads from the featuredArtists link on the Song collection to find all songs that reference a given artist. Both linked objects must already be persisted before you can save a link. Call save() on the link inside a write transaction after putting both objects:
await db.writeTxn(() async {
  await db.artists.put(artist);
  await db.songs.put(song);

  song.featuredArtists.add(artist);
  song.primaryArtist.value = artist;

  await song.featuredArtists.save();
  await song.primaryArtist.save();
});
For CindelLinks<T>, use add(object) to stage items before calling save(). For CindelLink<T>, assign directly to the value property.
Calling save() on a link replaces all currently persisted ids for that link with whatever is staged in memory. If you load a link, remove some items, and save, the removed items are gone from storage. Always persist linked objects before calling save() — Cindel uses the object’s database id when saving, and objects without a persisted id will produce incorrect results.
After reading an object from storage, call load() on each link field you want to access. Loading is explicit and asynchronous:
final storedSong = await db.songs.get(song.dbId);

await storedSong!.featuredArtists.load();
await storedSong.primaryArtist.load();
Once loaded, access the linked objects through the link fields:
  • CindelLink<T> — read through link.value, which is null when no object is linked or when the linked object has been deleted
  • CindelLinks<T> — iterate directly (it extends Iterable<T>) or call toList() for a snapshot
// To-one
final artist = storedSong.primaryArtist.value;

// To-many
final featured = storedSong.featuredArtists.toList();
@Backlink(to: 'forwardFieldName') marks a field as the read-only inverse of a forward link declared on the related collection. The to argument must match the Dart field name of the forward link:
@Backlink(to: 'featuredArtists')
final songs = CindelLinks<Song>();
Load a backlink the same way as a forward link:
final storedArtist = await db.artists.get(artist.dbId);
await storedArtist!.songs.load();

for (final song in storedArtist.songs) {
  print(song.title);
}
Backlinks are read-only. Calling save() on a backlink throws a StateError at runtime — the relationship is always managed from the forward side. To disconnect a linked object, update the forward link and call save() again. For CindelLink<T> set value to null. For CindelLinks<T> call remove(object) or clear the set and then call save():
await db.writeTxn(() async {
  final storedSong = await db.songs.get(song.dbId);
  await storedSong!.featuredArtists.load();
  storedSong.featuredArtists.remove(artist);
  await storedSong.featuredArtists.save();
});
Calling reset() clears the in-memory state without touching persisted ids — it is useful when you want to discard a locally staged change before calling save().

Build docs developers (and LLMs) love