Silo

Silo is an ORM-like library for Dart that blends key-value convenience with SQL-backed querying. Built on async_sqlite and sqlite, it provides a clean, extensible API through silo_common, with future support for more backends like sqflite.


Installation

Add to pubspec.yaml. For flutter applications, add sqlite3_flutter_libs to include the native SQLite library.


dependencies:
  silo: ^1.0.2
  sqlite3_flutter_libs: ^0.5.30 # For flutter applications

1. Open the Database

Start by opening or creating your database:

final db = await SiloDB.fromPath("z.db");

2. Key-Value Storage

Use silo<T>() for type-safe storage:

  // type safe operation
  // saves to a different table
  await db.silo<int>().put("first key", 4321);

  // value1 and value2 are not equal
  // because they are saved to different tables
  // even though they have the same key
  final value1 = await db.silo<int>().get("first key"); // 4321

  final value2 = await db.silo().get("first key"); // 1234

  print(value1 == value2); // false

You can also set expiration for entries:

await db.silo<Url>().put(
  "a url",
  Url(Uri(path: "/a/url")),
  expireAt: DateTime.now().add(Duration(hours: 2)),
);

3. Custom Types & Factory Registration

To store custom types like Uri, implement SiloValue:

class Url with SiloValue {
  final Uri uri;

  Url(this.uri);
  factory Url.parse(String v) => Url(Uri.parse(v));

  @override
  String toJson() => uri.toString();
}

Register them globally before use:

SiloRegistry.registerFactory(Url.parse);

4. Silo Tables & Migration

Define structured models with SiloTable<T>:

class Student with SiloTable<Student> {
  final String id, firstName, lastName;
  final Url profile;
  final int age;
  final School? school;

  Student({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.profile,
    required this.age,
    this.school,
  });

  factory Student.fromJson(Map<String, dynamic> json) => Student(
        id: json['id'],
        firstName: json['firstName'],
        lastName: json['lastName'],
        profile: Url.parse(json['profile']),
        age: json['age'],
        school: json['school'] != null
            ? School.fromJson(json['school'])
            : null,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'firstName': firstName,
        'lastName': lastName,
        'profile': profile.toJson(),
        'age': age,
        'school': school?.toJson(),
      };

  @override
  String tableKey() => "id";

  @override
  Map<String, dynamic> toMap() => toJson();
}

Create or update tables automatically:

await db.migrator.autoMigrateSiloTable(Student(
  id: "",
  firstName: "",
  lastName: "",
  profile: Url(Uri()),
  age: 0,
  school: School(id: "", name: ""),
));

Register factory with table name:

SiloRegistry.registerNamedFactory("students", Student.fromJson);

5. Insert & Query Structured Data

Insert model instances:

await db.silo<Student>().putSilo(student);

Perform queries:

final results = await db
    .silo<Student>()
    .eq('firstName', 'John')
    .gte('age', 18)
    .find()
    .values;

print(results.map((e) => e.toJson()));

Supports nested queries and logical chaining:

final students = await db
    .silo<Student>()
    .eq('id', 'id')
    .inList('lastName', ['James'])
    .where(
      db.silo<Student>()
        ..gte('age', 20)
        ..or(
          db.silo<Student>()..lt('age', 70),
        ),
    )
    .eq('school.id', 'schoolID')
    .find()
    .values;

Cleanup

Always close the database when done:

await db.close();

License

MIT

Libraries

silo
Silo Dart ORM