Typed Transactions
Compile-time-safe writes with db.txFor, fluent set, whole-model writes, and typed relations
Typed transactions (Phases 6c–6e) give a fluent, type-checked write builder over the
same operation engine as the untyped API. set<T>(Col<T>, T) binds each value's type to
its column, so wrong-typed writes (e.g. set(t.priority, 'x')) do not compile. All ops
delegate to the existing untyped builder — nothing is reimplemented.
Entry point
db.txFor(table) returns a TypedTx<E> for the table. The result is accepted directly
by db.transact (it implements ToTransaction).
final t = Todos();
await db.transact(
db.txFor(t).create(id: 't1')
..set(t.title, 'Run')
..set(t.priority, 3),
);Cascades (..set(..)..set(..)) are the idiomatic way to fill fields.
Operations
create / update / merge
// Create (id optional — generated if omitted)
db.txFor(t).create(id: 't1')..set(t.title, 'Run')..set(t.priority, 1);
// Update an existing entity
db.txFor(t).update('t1')..set(t.priority, 2);
// Deep-merge into an existing entity
db.txFor(t).merge('t1')..set(t.priority, 5);delete
delete returns a TransactionChunk directly (no set):
await db.transact(db.txFor(t).delete('t1'));Strict mode (TxOpts)
opts(TxOpts(upsert: false)) controls upsert/strict behavior on update / merge
(ignored for create). With upsert: false, the write does not create the entity if it
does not exist.
db.txFor(t).update('t1')
..set(t.priority, 9)
..opts(const TxOpts(upsert: false));Typed lookup (upsert by unique attribute)
lookup(Col, value) targets an entity by a unique attribute instead of by id — an
upsert. Pass merge: true to deep-merge instead of update.
// Update-or-create the todo whose email == 'a@b.com'
await db.transact(
db.txFor(t).lookup(t.email, 'a@b.com')..set(t.title, 'First'),
);
// Merge variant
db.txFor(t).lookup(t.email, 'a@b.com', merge: true)..set(t.title, 'X');link / unlink
Untyped relation link/unlink by attribute name (targetId is a single id or a List):
db.txFor(t).link('t1', 'tags', 'g1');
db.txFor(t).unlink('t1', 'tags', 'g1');For type-checked relations, prefer linkRel / unlinkRel (below).
Whole-model writes
When you use the generator, each table gets a scalar-only toMap, and a ${Model}TxX
extension adds whole-model write helpers:
createModel(Model)— writes all scalar fields in one call.data['id']from the model is used as the entity id.updateModel(id, Model)— writes all scalar fields of an existing entity.mergeModel(id, Model)— deep-merges all scalar fields.
await db.transact(db.txFor(GadgetTable()).createModel(
const Gadget(id: 'g1', label: 'Spanner'),
));
await db.transact(db.txFor(GadgetTable()).updateModel(
'g1',
const Gadget(id: 'g1', label: 'Wrench'),
));
await db.transact(db.txFor(GadgetTable()).mergeModel(
'g1',
const Gadget(id: 'g1', label: 'Hammer'),
));toMap is scalar-only. Every scalar field is included (id too); relation fields
are excluded. A model's relations are therefore not persisted by createModel —
write them with linkRel / unlinkRel.
Typed relation link / unlink
The generator emits a RelationRef const per relation (e.g. Widget2Table.gadgetsRel).
Pass it to linkRel / unlinkRel for compile-time-checked relation writes. targetIds
is a single id or a List.
// link two gadgets to a widget
await db.transact(
db.txFor(Widget2Table()).linkRel('w1', Widget2Table.gadgetsRel, ['g1', 'g2']),
);
// unlink one
await db.transact(
db.txFor(Widget2Table()).unlinkRel('w1', Widget2Table.gadgetsRel, 'g1'),
);The table.tx(db) sugar
Each generated table also emits a tx(db) method, so table.tx(db) is shorthand for
db.txFor(table):
await db.transact(
Widget2Table().tx(db).createModel(
const Widget2(id: 'w1', name: 'Box', weight: 3, gadgets: []),
),
);A model field literally named tx would collide with this method — a documented,
extremely rare edge.
Composing with untyped writes
Because db.transact accepts any ToTransaction, typed writes mix freely with the
untyped API in the same transaction:
await db.transact(
db.txFor(t).create()
..set(t.title, 'New')
..set(t.priority, 1),
);// Typed write alongside untyped chunk
await db.transact(db.txFor(t).update('t1')..set(t.priority, 2));
await db.transact(db.tx['todos']['t2'].update({'priority': 5}));