A strongly typed ORM for Dart inspired by Laravel Eloquent, bringing familiar patterns from Eloquent, GORM, SQLAlchemy, and ActiveRecord to Dart developers.
Part of the Routed ecosystem.
- Annotation-based models β Define tables, columns, and relationships with
@OrmModel,@OrmField,@OrmRelation - Code generation β Auto-generate model definitions, codecs, DTOs, and factories via
build_runner - Fluent query builder β Laravel-style API with
where,orderBy,join,limit, and more - Eager & lazy loading β Load relations upfront or on-demand, with nested paths (
'comments.author') - Lazy loading prevention β Catch N+1 queries in development with
ModelRelations.preventsLazyLoading - Aggregate loaders β Load counts, sums, averages without fetching full collections (
loadCount(),loadSum(), etc.) - Relation mutations β
associate(),attach(),detach(),sync()for managing relationships - Schema migrations β CLI tooling for creating, applying, and rolling back migrations
- Multi-database support β SQLite, PostgreSQL, MySQL/MariaDB
- Driver capabilities β Runtime feature detection for cross-database compatibility
- Multi-tenant connections β Manage multiple database connections with role-based routing
- Observability β Structured logging, query instrumentation, and tracing hooks
- Soft deletes β Built-in
SoftDeletesmixin with scoped queries - Repository pattern β Bulk inserts, upserts, and JSON updates
- Testing β Robust database isolation with
ormedGroupandormedTest. See the Testing Guide.
| Database | Package | Description |
|---|---|---|
| SQLite | ormed_sqlite | Via package:sqlite3 |
| PostgreSQL | ormed_postgres | Via package:postgres (v3) |
| MySQL / MariaDB | ormed_mysql | Via package:mysql_client_plus |
dart pub global activate ormed_clidependencies:
ormed: any
ormed_sqlite: any # or your preferred driver
dev_dependencies:
build_runner: ^2.4.0import 'package:ormed/ormed.dart';
part 'user.orm.dart';
@OrmModel(table: 'users')
class User {
const User({required this.id, required this.email, this.name});
@OrmField(isPrimaryKey: true, autoIncrement: true)
final int id;
@OrmField(isUnique: true)
final String email;
final String? name;
}dart run build_runner build --delete-conflicting-outputsimport 'package:ormed/ormed.dart';
import 'lib/src/database/datasource.dart'; // Generated by 'ormed init'
void main() async {
// Use the generated entrypoint
final ds = createDataSource();
await ds.init();
// Insert
await ds.repo<$User>().insert($UserInsertDto(
email: 'john@example.com',
name: 'John Doe',
));
// Query with eager loading
final users = await ds.query<$User>()
.withRelation('posts')
.withCount('posts', alias: 'post_count')
.orderByDesc('created_at')
.limit(10)
.get();
// Update
await ds.query<$User>()
.whereEquals('id', 1)
.update({'name': 'Jane Doe'});
// Eager load relations
final posts = await ds.query<$Post>()
.withRelation('author')
.withRelation('tags')
.withCount('comments')
.get();
// Lazy load relations
final post = await ds.query<$Post>().firstOrFail();
await post.load('author');
await post.loadMissing(['tags', 'comments']);
// Transaction
await ds.transaction(() async {
await ds.repo<$User>().insert(user1);
await ds.repo<$User>().insert(user2);
});
// Cleanup
await ds.dispose();
}Alternative: Manual Setup (advanced)
import 'package:ormed_sqlite/ormed_sqlite.dart';
void main() async {
final registry = ModelRegistry()..register(UserOrmDefinition.definition);
final adapter = SqliteDriverAdapter.file('app.sqlite');
final context = QueryContext(registry: registry, driver: adapter);
final users = await context.query<$User>()
.whereEquals('active', true)
.get();
}ormed init also writes lib/test/helpers/ormed_test_helper.dart. The helper wires up two SQLite DataSources via setUpOrmed (primary + analytics), runs the sample _CreateTestUsersTable migration, and exposes primaryTestConfig, analyticsTestConfig, and helpers such as primaryTestConnection(). Import it in your tests and pass the configs into ormedGroup or call primaryTestConnection() when you just need the default connection.
| Package | Description |
|---|---|
| ormed | Core ORM with annotations, query builder, migrations, codecs, and code generator |
| ormed_sqlite | SQLite driver adapter with JSON1, FTS5, and R*Tree support |
| ormed_postgres | PostgreSQL driver with full type support (UUID, JSONB, arrays, ranges, FTS) |
| ormed_mysql | MySQL/MariaDB driver with JSON, spatial types, and SET support |
| ormed_cli | CLI for migrations, seeding, schema operations, and project scaffolding |
| Package | Description |
|---|---|
| driver_tests | Shared driver-agnostic integration test suites |
| orm_playground | Demo application with end-to-end examples |
Note: These examples assume you have globally activated the CLI via
dart pub global activate ormed_cli. If not, usedart run ormed_cli:ormedinstead oformed.
# Initialize project structure
ormed init
# Create a new migration
ormed make --name create_users_table
# Run pending migrations
ormed migrate
# Preview migrations without executing
ormed migrate --pretend
# Rollback migrations
ormed migrate:rollback --steps 1
# Reset and re-run all migrations
ormed migrate:fresh
# Check migration status
ormed migrate:status
# Describe current schema
ormed schema:describe
# Run database seeders
dart run ormed_cli:ormed seed
dart run ormed_cli:ormed seed --class DemoContentSeeder
# Multi-tenant: apply to specific connection
dart run ormed_cli:ormed migrate --connection analyticsSee ormed_cli for complete documentation of all commands and options.
Define relationships with @OrmRelation:
@OrmModel(table: 'posts')
class Post {
final int id;
final int authorId;
final String title;
// Belongs to
@OrmRelation.belongsTo(related: User, foreignKey: 'author_id')
final User? author;
// Has many
@OrmRelation.hasMany(related: Comment, foreignKey: 'post_id')
final List<Comment> comments;
// Many to many
@OrmRelation.manyToMany(
related: Tag,
pivot: 'post_tags',
foreignPivotKey: 'post_id',
relatedPivotKey: 'tag_id',
)
final List<Tag> tags;
}Query with relations:
// Eager loading
final posts = await ds.query<$Post>()
.with_(['author', 'tags', 'comments.author'])
.get();
// Relation aggregates
final posts = await ds.query<$Post>()
.withCount('comments')
.withExists('tags')
.get();
// Filter by relation
final publishedWithComments = await ds.query<$Post>()
.whereHas('comments', (q) => q.whereEquals('approved', true))
.get();class CreatePostsTable extends Migration {
@override
void up(SchemaBuilder schema) {
schema.create('posts', (table) {
table.id();
table.integer('author_id').references('users', 'id');
table.string('title');
table.text('body').nullable();
table.boolean('published').defaultValue(false);
table.timestamps();
table.softDeletes();
table.index(['author_id']);
});
}
@override
void down(SchemaBuilder schema) {
schema.drop('posts');
}
}Full documentation is available at ormed.vercel.app.
- CLI Reference β Complete CLI commands and options
- Query Builder β Full query API reference
- Relations & Lazy Loading β Eager/lazy loading and relation mutations
- Migrations β Schema migrations and schema builder API
- Data Source β Runtime database access patterns
- Code Generation β Model annotations and generated code
- Model Factories β Test data generation and seeding
- Connectors β Connection management and multi-tenancy
- Observability β Logging, instrumentation, and tracing
- Driver Capabilities β Cross-database compatibility and feature detection
- Best Practices β Optimization strategies, patterns, and anti-patterns
Contributions are welcome! Please feel free to submit issues and pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you find this project helpful, consider supporting its development:
This project is licensed under the MIT License - see the LICENSE file for details.