bloom_db
Core ORM runtime and annotations for the Bloom framework. A QuerySet API
modeled on Django's ORM (and the djangors-orm Rust crate this port is based on), supporting both
SQLite and PostgreSQL symmetrically — pick either dialect per environment, not one primary/one
afterthought.
Features
QuerySet<T>chainable query API:filter,exclude,orderBy,limit,offset,get,first,all,exists,count,update,delete,bulkCreate,getOrCreate,updateOrCreate,values,valuesList.Q()expression objects for composable&/|/~filter conditions, andF()for field-to-field/field-to-expression updates (e.g.qty = qty - 1) without a round-trip read.- All filter values are bound parameters — never string-interpolated into SQL.
save()is INSERT-only (excludesautofields, usesRETURNING *); instanceupdate()/delete()operate by primary key and throw a typed not-found error on zero rows affected.@BloomModel/@BloomFieldannotations plus a hand-writtenModelMetafallback — models can be declared either way (seebloom_db_generatorfor the annotation-driven codegen path).PostgresDbExecutorandSqliteDbExecutor— sameQuerySet<T>code runs unmodified against either backend.
Usage
import 'package:bloom_db/bloom_db.dart';
@BloomModel(app: 'blog', tableName: 'blog_posts')
class Post extends Model {
@BloomField(primaryKey: true, auto: true, kind: FieldKind.bigInt)
final int id;
@BloomField(kind: FieldKind.char, maxLength: 255)
final String title;
Post({this.id = 0, required this.title});
static final meta = ModelMeta(
structName: 'Post',
appLabel: 'blog',
tableName: 'blog_posts',
fields: [
FieldMeta(name: 'id', columnName: 'id', kind: FieldKind.bigInt, primaryKey: true, auto: true),
FieldMeta(name: 'title', columnName: 'title', kind: FieldKind.char, maxLength: 255),
],
);
@override
ModelMeta get modelMeta => meta;
@override
List<(String, BloomValue)> fieldValues() => [
('id', BloomValue.i64(id)),
('title', BloomValue.text(title)),
];
static Post fromRow(DbRow row) => Post(
id: row.tryIntByName('id') ?? 0,
title: row.tryStringByName('title') ?? '',
);
static QuerySet<Post> objects() => QuerySet<Post>(meta: meta, fromRow: fromRow);
}
final db = await PostgresDbExecutor.connect(
host: '127.0.0.1',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'my_app',
);
final posts = await Post.objects().filter(Q('title__icontains', 'bloom')).orderBy('-id').limit(20).all(db);
For a server that handles concurrent requests, use the bounded PostgreSQL pool:
import 'dart:io';
import 'package:postgres/postgres.dart' as pg;
final db = PostgresDbExecutor.pooled(
host: '127.0.0.1',
port: 5432,
username: 'postgres',
password: Platform.environment['DATABASE_PASSWORD'],
database: 'my_app',
sslMode: pg.SslMode.verifyFull,
maxConnections: 12,
);
Pool connections open on demand and are reused after each query or transaction. Close the executor
during application shutdown to drain and close its connections. PostgresDbExecutor.connect()
remains available when the application needs a single dedicated connection.
Tests
The default test run covers SQLite and skips PostgreSQL integration tests:
dart test -p vm
To run the PostgreSQL contract and pooling tests, start PostgreSQL 16 with a bloom_db_test
database, postgres user, and postgres password, then opt in:
docker run --rm --detach --name bloom-db-test \\
-e POSTGRES_USER=postgres \\
-e POSTGRES_PASSWORD=postgres \\
-e POSTGRES_DB=bloom_db_test \\
-p 55432:5432 postgres:16-alpine
until docker exec bloom-db-test pg_isready -U postgres -d bloom_db_test; do sleep 1; done
BLOOM_TEST_POSTGRES=1 BLOOM_TEST_POSTGRES_PORT=55432 dart test -p vm -j 1
docker stop bloom-db-test
CircleCI provisions this service and sets BLOOM_TEST_POSTGRES=1, so its run always includes
the real PostgreSQL tests. Set the variable only when the configured test database is available;
these integration tests recreate their auth_users table. The test port defaults to 5432; set
BLOOM_TEST_POSTGRES_PORT when your local PostgreSQL service already uses that port.
Part of Bloom Server
bloom_db is one of the packages that make up Bloom Server, the backend stack for the Bloom
framework. Scaffold a full project with bloom server create <name> (from bloom_cli), or see
examples/bloom_fullstack_todo in the Bloom monorepo
for a reference project wiring every Bloom Server package together against real PostgreSQL.
License
MIT
Libraries
- bloom_db
- First-party database and ORM layer for the Bloom framework.