cindel 0.6.0 copy "cindel: ^0.6.0" to clipboard
cindel: ^0.6.0 copied to clipboard

Ultra-fast, lightweight NoSQL local database for Flutter and Dart apps, powered by a compact Rust native core.

Cindel

Flutter-first local database with generated Dart APIs and a compact Rust native core.

Typed collections · MDBX by default · SQLite optional · Native binaries for Flutter

Quickstart · Features · CRUD · Queries · Watchers · Embedded Objects · Native Binaries

At a Glance #

Area Cindel provides
Data model Typed collections generated from Dart classes
Storage MDBX by default, SQLite only when requested
Runtime Rust native core behind Dart FFI
Flutter support Prebuilt native binaries through cindel_flutter_libs
Queries Indexes, filters, sorting, pagination, projections, and aggregates
Reactivity Document, collection, object, query, and lazy watchers

Status #

Cindel is in active pre-1.0 development. APIs and storage format may still change before a stable release, so preview database files should be treated as disposable while the optimized native format settles.

Features #

Generated Dart API #

  • Typed collections from regular Dart model classes.
  • Freezed classic class and primary factory model support.
  • Schema metadata and compatible additive schema version bumps.

Storage and Runtime #

  • Rust native core behind Dart FFI.
  • MDBX default backend with SQLite as an explicit secondary backend.
  • Native auto-increment ids and in-memory databases for tests.
  • Bulk writes, reads, updates, and deletes.
  • Explicit read and write transactions.

Querying #

  • Equality, range, prefix, unique, hash, case-insensitive, word-token, composite, and primitive-list indexes.
  • Filter builders, sorting, pagination, distinct, property projections, and property aggregates.

Reactivity and Models #

  • Document, collection, object, query, and lazy watchers.
  • Embedded objects and embedded object lists.

Quickstart #

1. Add dependencies #

For Flutter apps, add Cindel plus the native library package:

dependencies:
  cindel: ^0.6.0
  cindel_flutter_libs: ^0.6.0

dev_dependencies:
  build_runner: ^2.15.0
  cindel_generator: ^0.6.0

Pure Dart projects can depend on cindel directly and provide a native library path with CINDEL_NATIVE_LIBRARY when needed.

2. Create a collection #

import 'package:cindel/cindel.dart';

part 'user.g.dart';

@Collection(name: 'users')
class User {
  Id dbId = autoIncrement;

  @Index(unique: true)
  late String email;

  @index
  late String name;

  bool active = true;

  DateTime createdAt = DateTime.now().toUtc();
}

Freezed classic classes

Cindel can also generate schemas for Freezed classic classes. This lets you keep immutable models while Freezed provides copyWith, equality, and hashCode:

Freezed models also need freezed_annotation as an app dependency and freezed as a development dependency.

import 'package:cindel/cindel.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
@Collection(name: 'users')
class User with _$User {
  const User({
    required this.dbId,
    required this.email,
    required this.name,
    this.active = true,
  });

  @override
  final Id dbId;

  @override
  @Index(unique: true)
  final String email;

  @override
  final String name;

  @override
  final bool active;
}

Cindel also supports the common Freezed primary factory style:

@freezed
@Collection(name: 'users')
abstract class User with _$User {
  const factory User({
    required Id dbId,
    required String email,
    required String name,
    @Default(true) bool active,
  }) = _User;
}

IMPORTANT: Freezed union/sealed multi-constructor models are not supported.

3. Generate code #

dart run build_runner build --delete-conflicting-outputs

4. Open a database #

final db = await Cindel.open(
  directory: directory.path,
  schemas: [UserSchema],
);

MDBX is the default backend. SQLite is available only when requested explicitly:

final db = await Cindel.open(
  directory: directory.path,
  schemas: [UserSchema],
  backend: CindelStorageBackend.sqlite,
);

For tests and short-lived work:

final db = await Cindel.openInMemory(schemas: [UserSchema]);

CRUD #

Generated collections are available directly from the database handle:

final jhon = User()
  ..name = 'Jhon Doe'
  ..email = 'jhon@example.com'
  ..active = true;

await db.users.put(jhon);

final saved = await db.users.get(jhon.dbId);

await db.users.delete(jhon.dbId);

Bulk operations use native batch paths:

final maria = User()
  ..name = 'Maria Cruz'
  ..email = 'maria@example.com';

final taylor = User()
  ..name = 'Taylor Reed'
  ..email = 'taylor@example.com';

await db.users.putMany([jhon, maria, taylor]);

final users = await db.users.getAll([jhon.dbId, maria.dbId, 404]);

await db.users.deleteAll([jhon.dbId, maria.dbId]);

Use transactions when multiple operations must commit together:

await db.writeTxn(() async {
  await db.users.put(jhon);
  await db.users.put(maria);
});

final activeUsers = await db.readTxn(() {
  return db.users.filter().activeEqualTo(true).findAll();
});

Queries #

Generated query builders start from indexed where clauses or collection filters:

final jhon = await db.users
    .where()
    .emailEqualTo('jhon@example.com')
    .findFirst();

final activeUsers = await db.users
    .filter()
    .activeEqualTo(true)
    .sortByName()
    .findAll();

String indexes get prefix helpers:

final people = await db.users.where().nameStartsWith('Jh').findAll();

Queries support counts, deletes, pagination, distinct values, property projections, and aggregates:

final count = await db.users.filter().activeEqualTo(true).count();

final names = await db.users
    .filter()
    .activeEqualTo(true)
    .sortByName()
    .distinctByEmail()
    .nameProperty()
    .findAll();

final maxId = await db.users.all().dbIdProperty().max();

List fields expose Isar-style element and length helpers:

final flutterUsers = await db.users
    .filter()
    .tagsElementEqualTo('flutter')
    .findAll();

final usersWithoutTags = await db.users.filter().tagsIsEmpty().findAll();

final usersWithOneOrTwoTags = await db.users
    .filter()
    .tagsLengthBetween(1, 2)
    .findAll();

The lower-level manual document API remains available:

await db.put('users', 1, {
  'name': 'Jhon Doe',
  'email': 'jhon@example.com',
  'active': true,
});

final document = await db.get('users', 1);

Watchers #

Cindel watchers expose Dart streams for objects, collections, queries, and manual documents:

final sub = db.users
    .filter()
    .activeEqualTo(true)
    .sortByName()
    .watch()
    .listen((users) {
      // Rebuild visible state.
    });

Lazy watchers emit only an invalidation signal:

final sub = db.users.watchCollectionLazy().listen((_) {
  // Refresh cached state.
});

Embedded Objects #

Use @Embedded or @embedded for value objects stored inside a parent collection document. Embedded objects are not root collections; they are serialized as part of the parent object.

@Collection(name: 'emails')
class Email {
  Id dbId = autoIncrement;

  String? subject;

  Recipient? sender;

  List<Recipient>? recipients;
}

@Embedded()
class Recipient {
  String? name;
  String? address;
  RecipientMetadata? metadata;
}

@embedded
class RecipientMetadata {
  String? label;
}

Generated filters can query fields inside a single embedded object, including nested embedded objects:

final messages = await db.emails
    .filter()
    .sender((recipient) => recipient.addressEqualTo('ada@example.com'))
    .findAll();

final leadMessages = await db.emails
    .filter()
    .sender((recipient) {
      return recipient.metadata((metadata) {
        return metadata.labelEqualTo('lead');
      });
    })
    .findAll();

final maryMessages = await db.emails
    .filter()
    .recipientsElement((recipient) {
      return recipient.addressEqualTo('mary@example.com');
    })
    .findAll();

final secondaryMessages = await db.emails
    .filter()
    .recipientsElement((recipient) {
      return recipient.metadata((metadata) {
        return metadata.labelEqualTo('secondary');
      });
    })
    .findAll();

Embedded object fields and embedded object lists can be stored through the native typed document path. The generated writer uses native object/list hooks, and the generated reader hydrates embedded objects without requiring a manual JSON round-trip.

Generated helpers support equality for the whole embedded value and element equality for embedded lists. Nested field filter helpers are generated for single embedded object fields and for elements inside embedded object lists.

Embedded indexes are not supported. Put @Index on root collection fields, not inside @Embedded classes.

Supported Platforms #

The current package line ships prebuilt Flutter libraries for Android, iOS, Linux, macOS, and Windows through cindel_flutter_libs.

Native Binaries #

Flutter apps should depend on cindel_flutter_libs so the native runtime is bundled automatically.

When testing a custom local build, set CINDEL_NATIVE_LIBRARY to an absolute path before running Dart tests or tools:

$env:CINDEL_NATIVE_LIBRARY = 'D:\path\to\cindel_native.dll'

Unit Tests #

Use in-memory databases for fast tests:

final db = await Cindel.openInMemory(schemas: [UserSchema]);
addTearDown(db.close);

Benchmarks #

Benchmarks are a rough signal rather than an absolute performance guarantee, but they are useful for tracking whether changes move Cindel in the right direction. The charts below compare the current app-style benchmark in both small and larger payload modes.

Small Payloads #

big=false

Cindel benchmark chart for small payloads

Larger Payloads #

big=true

Cindel benchmark chart for larger payloads

If you want to inspect more benchmark cases or check how Cindel performs on your device, you can run the benchmarks yourself.

License #

Cindel is licensed under the Apache License, Version 2.0. See the repository license file for details.

3
likes
0
points
868
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

Ultra-fast, lightweight NoSQL local database for Flutter and Dart apps, powered by a compact Rust native core.

Repository (GitHub)
View/report issues

Topics

#database #nosql #storage #cindel #encryption

License

unknown (license)

Dependencies

cindel_annotations, ffi, hooks, native_toolchain_rust

More

Packages that depend on cindel