Skip to content
This repository has been archived by the owner on Feb 17, 2020. It is now read-only.


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Pub build status

Source-generated PostgreSQL ORM for use with the Angel framework. Now you can combine the power and flexibility of Angel with a strongly-typed ORM.

Documentation for migrations can be found here:


You'll need these dependencies in your pubspec.yaml:

  angel_orm: ^2.0.0-dev
  angel_orm_generator: ^2.0.0-dev
  build_runner: ^1.0.0

package:angel_orm_generator exports a class that you can include in a package:build flow:

  • PostgresOrmGenerator - Fueled by package:source_gen; include this within a SharedPartBuilder.

However, it also includes a build.yaml that builds ORM files automatically, so you shouldn't have to do any configuration at all.


The ORM works best when used with package:angel_serialize:


import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'car.g.dart';

abstract class _Car extends Model {
  String get make;

  String get description;

  bool get familyFriendly;

  DateTime get recalledAt;

// You can disable migration generation.
@Orm(generateMigrations: false)
abstract class _NoMigrations extends Model {}

Models can use the @SerializableField() annotation; package:angel_orm obeys it.

After building, you'll have access to a Query class with strongly-typed methods that allow to run asynchronous queries without a headache.

Remember that if you don't need automatic id-and-date fields, you can simply just not extend Model:

@Serializable(autoIdAndDateFields: false)
abstract class _ThisIsNotAnAngelModel {
  String get username;


MVC just got a whole lot easier:

import 'package:angel_framework/angel_framework.dart';
import 'package:angel_orm/angel_orm.dart';
import 'car.dart';
import 'car.orm.g.dart';

/// Returns an Angel plug-in that connects to a database, and sets up a controller connected to it...
AngelConfigurer connectToCarsTable(QueryExecutor executor) {
  return (Angel app) async {
    // Register the connection with Angel's dependency injection system.
    // This means that we can use it as a parameter in routes and controllers.
    // Attach the controller we create below
    await app.mountController<CarController>();

class CarController extends Controller {
  // The `executor` will be injected.
  carsRecalledSince2008(QueryExecutor executor) {
    // Instantiate a Car query, which is auto-generated. This class helps us build fluent queries easily.
    var query = new CarQuery();
    // Shorter syntax we could use instead...
    query.where.recalledAt.year <= 2008;
    // `get()` returns a Future<List<Car>>.
    var cars = await query.get(executor);
    return cars;
  @Expose('/create', method: 'POST')
  createCar(QueryExecutor executor) async {
    // `package:angel_orm` generates a strongly-typed `insert` function on the query class.
    // Say goodbye to typos!!!
    var query = new CarQuery();
      ..familyFriendly = true
      ..make 'Honda';
    var car = query.insert(executor);
    // Auto-serialized using code generated by `package:angel_serialize`
    return car;


angel_orm supports the following relationships:

  • @HasOne() (one-to-one)
  • @HasMany() (one-to-many)
  • @BelongsTo() (one-to-one)
  • @ManyToMany() (many-to-many, using a "pivot" table)

The annotations can be abbreviated with the default options (ex. @hasOne), or supplied with custom parameters (ex. @HasOne(foreignKey: 'foreign_id')).

abstract class _Author extends Model {
  @HasMany // Use the defaults, and auto-compute `foreignKey`
  List<_Book> books;
  // Also supports parameters...
  @HasMany(localKey: 'id', foreignKey: 'author_id', cascadeOnDelete: true)
  List<_Book> books;
  @SerializableField(alias: 'writing_utensil')
  _Pen pen;

The relationships will "just work" out-of-the-box, following any operation. For example, after fetching an Author from the database in the above example, the books field would be populated with a set of deserialized Book objects, also fetched from the database.

Relationships use joins when possible, but in the case of @HasMany(), two queries are used:

  • One to fetch the object itself
  • One to fetch a list of related objects

Many to Many Relations

A many-to-many relationship can now be modeled like so. RoleUser in this case is a pivot table joining User and Role.

Note that in this case, the models must reference the private classes (_User, etc.), because the canonical versions (User, etc.) are not-yet-generated:

abstract class _User extends Model {
  String get username;
  String get password;
  String get email;

  List<_Role> get roles;

abstract class _RoleUser {
  _Role get role;

  _User get user;

abstract class _Role extends Model {
  String name;

  List<_User> get users;


  1. Make a pivot table, C, between two tables, table A and B
  2. C should @belongsTo both A and B. C should not extend Model.
  3. A should have a field: @ManyToMany(_C) List<_B> get b;
  4. B should have a field: @ManyToMany(_C) List<_A> get a;



Use a @Column() annotation to change how a given field is handled within the ORM.

Column Types

Using the @Column() annotation, it is possible to explicitly declare the data type of any given field:

abstract class _Foo extends Model {
  @Column(type: ColumnType.bigInt)
  int bar;


Columns can also have an index:

abstract class _Foo extends Model {
  @Column(index: IndexType.primaryKey)
  String bar;

Default Values

It is also possible to specify the default value of a field. Note that this only works with primitive objects.

If a default value is supplied, the SqlMigrationBuilder will include it in the generated schema. The PostgresOrmGenerator ignores default values; it does not need them to function properly.

abstract class _Foo extends Model {
  @Column(defaultValue: 'baz')
  String bar;