mrkdb 1.1.0
mrkdb: ^1.1.0 copied to clipboard
A lightweight encrypted NoSQL local database for Flutter & Dart with schema validation, indexing, and query support.
mrkdb #
A lightweight, encrypted, schema-validated NoSQL local database for
Flutter and Dart. No native code, no SQL, no platform channels β just a
single .mrkdb file (or localStorage entry on web), encrypted at rest
with AES-256-GCM.
Features #
- π Encrypted at rest β every database is AES-256-GCM encrypted, key derived from your password via PBKDF2.
- π§© Schema validation β optional
KWallschemas enforce required fields, types, trimming, and automaticcreatedAt/updatedAttimestamps. - π Unique indexes β single-field and compound unique constraints.
- π Fluent queries β chainable
where,contains,greaterThan,orderBy,skip,limit. - π¦ Zero native dependencies β pure Dart, works everywhere Flutter runs.
- π₯οΈ Every platform β Android, iOS, Windows, macOS, Linux, and Web.
Platform support #
| Android | iOS | Windows | macOS | Linux | Web |
|---|---|---|---|---|---|
| β | β | β | β | β | β |
Install #
flutter pub add mrkdb
Quick start #
import 'package:mrkdb/mrkdb.dart';
// Open (or create) a database. The password derives the encryption key.
final db = await MrkDB.open(name: 'appdata', password: 'super-secret');
// A collection ("KHeart") with an optional schema ("KWall").
final users = db.kheart('users').withKWall(const KWall(
timestamps: true,
fields: {
'email': KWallField(type: String, required: true, unique: true, trim: true),
'age': KWallField(type: int),
},
));
// Insert.
final id = await users.insertCell({'email': 'ada@example.com', 'age': 30});
// Query.
final adults = users.query()
.greaterThan('age', 17)
.orderBy('age', descending: true)
.find();
// Update / delete.
await users.updateCell(id, {'age': 31});
await users.deleteCell(id);
See example/ for a complete app built on top of mrkdb.
Collections (KHeart) #
Every database is made of named collections, each holding JSON-like
documents (Map<String, dynamic>). Look one up (creating it if needed)
with db.kheart('name'). Every document gets a generated _id on insert.
final orders = db.kheart('orders');
await orders.insertCell({'total': 42.5, 'status': 'pending'});
await orders.insertCells([{'total': 10}, {'total': 20}]);
orders.getAll();
orders.firstWhere('status', 'pending');
orders.exists('status', 'pending');
orders.count();
orders.sum('total');
orders.average('total');
orders.paginate(page: 1, limit: 20);
Schema validation (KWall) #
Attaching a KWall to a collection is optional, but gives you required
fields, type checking, string trimming, automatic timestamps, and unique
indexes:
const employeeWall = KWall(
timestamps: true,
compoundUnique: [
['name', 'department'], // no two employees share both
],
fields: {
'name': KWallField(type: String, required: true, trim: true),
'department': KWallField(type: String, required: true, trim: true),
'salary': KWallField(type: int, required: true),
},
);
db.kheart('employees').withKWall(employeeWall);
withKWall is safe to call every time you look up the collection β it
just re-attaches the same schema and re-registers its indexes.
Querying #
KHeart.query() returns a chainable, immutable-until-find() query
builder:
final results = users.query()
.where('status', 'active')
.contains('email', '@example.com')
.orderBy('createdAt', descending: true)
.skip(0)
.limit(20)
.find();
Error handling #
MrkDB.open throws an MrkDbException if a database already exists but
can't be decrypted β most commonly a wrong password, or a corrupted file.
A brand-new database (nothing saved yet under that name) simply opens
empty and throws nothing.
KHeart mutation methods (insertCell, insertCells, updateCell) throw
a plain Exception when a KWall validation rule is violated β a missing
required field, a type mismatch, or a duplicate value on a unique index.
try {
await users.insertCell({'email': 'ada@example.com'}); // duplicate email
} catch (e) {
// Handle the validation/unique-index failure.
}
License #
MIT β see LICENSE.