⚠️ ALPHA — This project is in early development. APIs may change. Use in production at your own risk.
A Dapper-style database toolkit for Dart — SQL-first, simple mapping, native performance via Rust FFI.
- SQL-first — Write real SQL. No DSL to learn, no magic strings.
- Simple mapping —
fromJson/toJsonconventions, compatible with Vaden DTOs. - Native performance — Rust connectors via FFI. No pure-Dart protocol parsing.
- Pick your database — One package per driver. Only import what you use.
- Query builder included — Optional fluent API when you don't want raw SQL.
- Migrations — Plain
.sqlfiles, automatic tracking.
| Package | Database | Status |
|---|---|---|
anaki_orm |
Core (pure Dart) | ✅ Ready |
anaki_sqlite |
SQLite | ✅ Ready |
anaki_postgres |
PostgreSQL | ✅ Ready |
anaki_mysql |
MySQL | ✅ Ready |
anaki_mssql |
SQL Server | ✅ Ready |
anaki_oracle |
Oracle | 🔜 Deferred |
# pubspec.yaml
dependencies:
anaki_orm: ^0.1.0
anaki_sqlite: ^0.1.0 # or anaki_postgres, anaki_mysql, anaki_mssqlimport 'package:anaki_orm/anaki_orm.dart';
import 'package:anaki_sqlite/anaki_sqlite.dart';
void main() async {
final db = AnakiDb(SqliteDriver(':memory:'));
await db.open();
// Raw SQL — always available
await db.execute(
'INSERT INTO users (name, email) VALUES (@name, @email)',
{'name': 'Ana', 'email': 'ana@example.com'},
);
// Query with mapping
final users = await db.query(
'SELECT * FROM users',
null,
UserDTO.fromJson,
);
// Or use the query builder
final qb = AnakiQueryBuilder(db, adapter);
final active = await qb.select<UserDTO>('users')
.where('active = @active', {'active': true})
.orderBy('name')
.list();
await db.close();
}📖 Full API docs → packages/anaki_orm/README.md
final rows = await db.query('SELECT * FROM users');
final first = await db.queryFirst('SELECT * FROM users WHERE id = @id', {'id': 1});
final count = await db.scalar<int>('SELECT COUNT(*) FROM users');
final affected = await db.execute('DELETE FROM users WHERE active = 0');
await db.transaction((tx) async {
await tx.execute('UPDATE accounts SET balance = balance - 100 WHERE id = @from', {'from': 1});
await tx.execute('UPDATE accounts SET balance = balance + 100 WHERE id = @to', {'to': 2});
});final qb = AnakiQueryBuilder(db, adapter);
await qb.select<UserDTO>('users').where('active = @a', {'a': true}).list();
await qb.insert<UserDTO>('users').entity(user).run();
await qb.update('users').set({'email': 'new@x.com'}).where('id = @id', {'id': 1}).run();
await qb.delete('users').where('id = @id', {'id': 1}).run();await Migrator(db).run('migrations/');
// Reads 001_create_users.sql, 002_add_index.sql, ... in orderawait Seeder(db).run('seeds/');
// Reads 001_seed_users.sql, 002_seed_products.sql, ... in orderAnaki uses native libraries (.so, .dylib, .dll) that must be available at runtime. For Docker or AOT-compiled applications, you need to include the native library in your container.
# 1. Build the native library for Linux
./scripts/build_native.sh sqlite # or postgres, mysql, mssql, all
# 2. Copy the library to your Docker build context
cp packages/anaki_sqlite/native_libs/libanaki_sqlite-linux-x64.so ./docker/FROM dart:stable AS build
WORKDIR /app
COPY . .
RUN dart pub get
RUN dart compile exe bin/server.dart -o bin/server
FROM scratch
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/bin/server
# Copy the native library
COPY --from=build /app/docker/libanaki_sqlite-linux-x64.so /app/bin/
WORKDIR /app/bin
CMD ["./server"]- The native library must be in the same directory as the executable, or in a system library path
- For Linux containers, use the
-linux-x64.sovariant - See docs/build.md for complete build instructions
See CONTRIBUTING.md for project structure, how to build native libraries, run tests, and add new drivers.
MIT