Skip to content

Records

List, iterate, read, create, update, delete and aggregate records with the Dart SDK; filters, ETags, batches, singletons, custom routes and cancelling.

vb.collection(name) returns the collection’s records as RecordModels. With generated types, vb.collections.posts returns them as PostsRecords; everything below works the same.

final page = await vb.collection('posts').list(
filter: "status = 'published'",
sort: '-created',
expand: 'author',
fields: 'id,title,author',
// or leave a few out instead (server 0.31.0+): exclude: 'body,meta',
page: 1,
perPage: 50, // the server caps it at 500
skipTotal: true, // no count: faster; totals are -1
);
page.items; // List<RecordModel>
page.totalItems; // -1 with skipTotal
await for (final post in vb.collection('posts').iterate(filter: '...')) {
// every match, fetched a page at a time
}

search: takes a full-text query for collections with search on.

Never paste user input into a filter. vb.filter fills {:name} placeholders, escaped by type:

final f = vb.filter('title ~ {:q} && views > {:n} && created > {:since}', {
'q': query, // strings quoted and escaped
'n': 10, // numbers bare
'since': DateTime.utc(2026, 1, 1),// ISO date
});
// A list throws (vaultbase 0.6.0+): the filter language has no list value,
// so join one comparison per value with ||.
vb.filter('{:f} = 1', {'f': Field('meta.count')}); // a checked field name
final post = await vb.collection('posts').get(id, expand: 'author');
final created = await vb.collection('posts').create({'title': 'Hi'});
final updated = await vb.collection('posts').update(id, {'title': 'Edited'});
await vb.collection('posts').delete(id);
post.id;
post.createdAt; // DateTime (UTC); post.created is Unix seconds
post['title']; // the raw value
post.getString('title'); // '' when unset
post.getInt('views'); // 0 when unset
post.getDouble('rating');
post.getBool('published'); // also reads auth fields' 0 / 1
post.getList<String>('tags');
post.getDate('publish_at'); // DateTime?, from Unix seconds
post.expandOne('author'); // RecordModel? from expand
post.expandMany('comments');
post.data; // the whole map

For your own classes, vb.collectionOf('posts', Post.fromJson) decodes each record with your fromJson.

A view collection with parameters takes their values as params: on list, iterate, get and aggregate (server 0.35.0+). A DateTime is sent as UTC ISO 8601.

final top = await vb.collection('customer_revenue').list(
params: {'since': DateTime.utc(2026, 10, 1), 'status': 'paid'},
sort: '-revenue',
);

dart run vaultbase:types emits a class per view with parameters, required ones required:

final top = await vb.collections.customerRevenue.list(
params: CustomerRevenueParams(status: 'paid', since: DateTime.utc(2026, 10, 1)).toMap(),
);

A read keeps the record’s ETag. update and delete send it as If-Match, so if someone changed the record since you read it, they throw PreconditionFailedException instead of overwriting the change:

try {
await vb.collection('posts').update(id, {'title': 'Mine'});
} on PreconditionFailedException {
// reload, merge, try again
}

Pass ifMatch: to send a specific ETag, or checkEtag: false to send none.

final rows = await vb.collection('posts').aggregate(
agg: ['count', 'avg(views)'],
groupBy: ['status'],
filter: "created > '2026-01-01'",
);
// [{status: published, count: 12, avg_views: 40.5}, ...]

count, count(f), sum(f), avg(f), min(f), max(f).

Up to 100 operations in one request, run in one transaction:

final results = await (vb.batch()
..create('posts', {'title': 'One'})
..update('posts', id, {'title': 'Two'})
..delete('posts', oldId))
.run();
results.first.status; // per operation, in order
results.first.body;

A batch that writes can’t contain a list: the server answers 422 before anything runs. Send lists in a batch of reads (get and list only). A get by id works in a write batch.

final site = await vb.singleton('site').get();
await vb.singleton('site').update({'headline': 'Hello'}); // If-Match as above
final hello = await vb.custom.get('hello', query: {'name': 'Ann'});
await vb.custom.post('orders', body: {'sku': 'a1'});
final raw = await vb.send('/api/v1/health'); // with the session and error mapping

Every request method takes a CancelToken:

final cancel = CancelToken();
final pending = vb.collection('posts').list(cancelToken: cancel);
cancel.cancel(); // pending throws CancelledException

Reads also take a requestKey: a newer request with the same key cancels the older one, for search-as-you-type. Vaultbase(..., autoCancel: true) gives every request a key from its method and path. vb.cancelAll() cancels every keyed request.