Working with Loxia
I can admit I’ve traditionally stuck to NoSQL databases. The simplicity of organizing data into documents and collections made development feel much easier. But I would be lying if I said that fear of SQL, especially as someone who primarily works with Flutter, did not influence that decision. The idea of combining such a structured language with something as lightweight as Dart kept me from exploring further. However, I recently stumbled upon Loxia, an ORM that brings simplicity and efficiency to Dart developers. It supports SQLite, PostgreSQL, and MySQL with ease. So what makes it stand out?
Setting Up Entities
Loxia is a product of Avesbox, a team building tools and open-source projects for the Dart community. With several ongoing projects under the same umbrella, I felt confident giving it a try.
This is the first ORM I’ve used (as far as I know), so I don’t have much to compare it to. Still, it seems to work like most ORMs: you define your entity, generate code, and get the APIs you need to communicate with your database. For example, in the Camp Central app I’m working on, I create an entity for a camp.
import 'package:loxia/loxia.dart';
part 'camp.g.dart';
@EntityMeta(
table: 'camps',
indexes: [
Index(columns: ['registration_url'], unique: true),
],
)
class Camp extends Entity {
@PrimaryKey(uuid: true)
final String id;
@Column()
final String name;
@Column(name: 'registration_url')
final String registrationUrl;
@Column()
@CreatedAt()
DateTime? createdAt;
Camp({
required this.id,
required this.name,
required this.registrationUrl,
this.createdAt,
});
static EntityDescriptor<Camp, CampPartial> get entity =>
$CampEntityDescriptor;
}
A few cool things I like here:
-
Setting up unique constraints on columns via the
indexesproperty on theEntityMeta. -
Choosing a primary key strategy:
autoIncrementfor auto-integer IDs, oruuidfor auto-string IDs. -
Specifying column names with the
@Columnannotation, similar to@JsonKeywhen using Freezed. -
Special annotations such as
@CreatedAt(and also@UpdatedAtand@DeletedAt) that atomically save a DateTime on row insert.
Using APIs from Loxia
Once the entity has been established, run the build runner command to generate the repository.
flutter pub run build_runner build --delete-conflicting-outputs
And voilà, a flexible repository at your fingertips. Next, you can create a service layer that talks to the repository and handles initializing the repo and configuring the data source.
class DatabaseConfig {
DatabaseConfig._();
static final ds = DataSource(
PostgresDataSourceOptions.fromUrl(
url: "[CONNECTION_URL]",
entities: [Camp.entity],
),
);
}
class CampService {
final _campRepository = DatabaseConfig.ds.getRepository<Camp>();
}
Some of the methods provided are the usual findBy and delete methods, but there are also methods like
count (for getting the row count for a query) and paginate (returns a subset of rows based on the query and current page index).
Future<int> count() async => _campRepository.count();
Future<List<Camp>> paginate({required int page}) async {
final result = await _campRepository.paginate(
page: page,
pageSize: 20,
orderBy: [const OrderBy('created_at', ascending: false)],
);
return result.items.map((i) => i.toEntity()).toList();
}
The Future Is Bright
This recap is a high-level look at how Loxia works, but the rabbit hole goes much deeper. Features like lifecycle hooks, migrations, and table relations make it more than an ordinary ORM. The developer himself (Francesco Vallone) has also been very responsive and helpful with any issues I ran into while coding. It seems to be a fairly new framework, but I have not seen anything that would turn me away from using it long term in other projects. Plus, it never hurts to learn more SQL.
Thanks for reading
I hope you found this article helpful—if so, please share it!
Coffee Break: White Angel
While wrapping up this article, I enjoyed a White Angel from Mr. Brew — white chocolate and coconut in a white coffee. Solid concept, but it just wasn't for me. It was missing a certain sweetness, although maybe it would've been better as a latte. It did its job as an evening caffeine fix, though.
6/10