What Cindel is
Cindel is not a remote backend or a sync service. It is an embedded database that lives inside your app and makes local structured data available quickly and with full Dart type safety. It is useful when your app needs:- offline-capable structured data that survives app restarts,
- cached records that are still queryable without a network connection,
- local-first workflows where writes happen on-device before being synced,
- durable UI state such as task lists, product catalogs, settings, or message threads.
The code-generation model
Cindel’s public API is entirely generated. The workflow is:- Annotate a class. Add
@Collection()(or@collection) to a Dart class, declare anId dbId = autoIncrementfield, and mark indexed fields with@Index. - Add a
partdirective. Includepart 'modelname.g.dart';in the model file so the generator can attach the output. - Run
build_runner. Executedart run build_runner buildto produce the schema constant, typed collection getter, serializers, and query helpers. - Use the generated API. Register the generated schema when you open the database, then read and write through the typed collection getter.
user.dart
UserSchema and uses db.users. Every method on db.users is typed to User:
db.users.put(user)accepts aUser.db.users.get(id)returns aFuture<User?>.- Query helpers such as
db.users.filter().activeEqualTo(true).findAll()are generated from your model’s field names. - Invalid field names cannot be passed as plain strings in everyday app code.
Available backends
Cindel selects its storage backend at open time. The application-level API stays the same regardless of which backend is active.
Use MDBX for new native apps unless you have a specific reason to prefer SQLite. Both backends expose the same schemas, collection getters, queries, transactions, and watchers — switching backends does not require rewriting application code.
On the web,
cindel_flutter_libs bundles the SQLite Worker and Wasm assets. The directory parameter in Cindel.open becomes a logical browser database name rather than a filesystem path.
Core query entry points
Every generated collection exposes three query starting points:all()— starts from the entire collection with no filter.where()— uses generated helpers for fields marked with@Index; ideal for high-performance indexed lookups.filter()— uses generated helpers for any stored field; supports rich predicates, string matching, list element queries, and boolean composition.
where() and filter() chains support sorting, pagination, distinct values, projections, aggregates, query-scoped updates, and query-scoped deletes. Watchers can be attached to any query chain.
Reactive watchers
Cindel can emit a fresh typed snapshot every time matching data changes. You attach a watcher to an object, a collection, or a query chain, and your stream listener receives the updated result without polling.When to use Cindel vs. a remote database
Use Cindel when your app needs fast, typed, queryable data that lives on the device — task lists, product catalogs, draft content, user preferences, or anything that should work offline. Use a remote database (or add Cindel’s experimental sync) when authoritative data must be shared across devices or users, or when the server is the system of record. The two approaches are complementary: many apps store a local Cindel database for offline performance and sync changes to a backend when connectivity is available.Next steps
Quickstart
Follow the step-by-step guide to define a model, generate code, and run your first typed query in a Flutter app.