Explore the use of Aqueduct, the Dart server-side framework, to bind a REST Interface to Postgres, producing more efficient results.

In the Dart server-side framework Aqueduct, a ManagedObjectController<T> automatically maps the standard HTTP methods to a database query. That is, adding the following route:

router .route("/employees/[:id]") .generate(() => new ManagedObjectController<Employee>());

Gives us the following endpoints:

Method Path Behavior POST /employees Creates a new employee GET /employees Gets all employees GET /employees/:id Gets one employee by ID PUT /employees/:id Updates one employee by ID DELETE /employees/:id Deletes one employee by ID

(See a working example here.)

The endpoint to get all employees has some options for modifying which objects are returned. These are passed as query parameters. Simple paging looks like this:

GET /employees?count=10&offset=10

Property-based paging looks like this:

// Gets the first 10 hired employees GET /employees?count=10&pageBy=hireDate&pageAfter=null // Get the next 10 hired employees after 7/27/17 GET /employees?count=10&pageBy=hireDate&pageAfter=2017-07-27T00:00:00Z

And the results can be sorted by one or more properties, too:

GET /employees?sortBy=lastName,asc GET /employees?sortBy=lastName,asc&sortBy=firstName,desc

Sometimes, this simple REST-to-table binding is enough. Other times, you may need to add a bit more logic by subclassing ManagedObjectController<T> . For example, let’s say we want to also include an employee’s manager along with the employee, but only when we fetch a single employee:

class EmployeeController extends ManagedObjectController<Employee> { @override Future<Query<Employee>> willFindObjectWithQuery(Query<Employee> query) async { query.join(object: (employee) => employee.manager); return query; } }

Or let’s say we’d like to filter by some property of an employee when fetching many:

class EmployeeController extends ManagedObjectController<Employee> { @override Future<Query<Employee>> willFindObjectsWithQuery(Query<Employee> query) async { var titleFilter = request.innerRequest.uri.queryParameters["title"]; if (titleFilter != null) { query.where.title = whereEqualTo(titleFilter); } return query; } }

You could do this for generically for any property with a little magic:

class EmployeeController extends ManagedObjectController<Employee> { @override Future<Query<Employee>> willFindObjectsWithQuery(Query<Employee> query) async { request.innerRequest.uri.queryParameters.forEach((property, value) { if (query.entity.attributes[property] != null) { query.where[property] = whereEqualTo(value); } }); return query; } }

If your needs outgrow ManagedObjectController<T> , then the ubiquitous HTTPController is the next step. This process of starting with higher-leveled features and gradually descending closer to the core is a key design principle of Aqueduct.

For even more fun, you can dynamically create a REST interface for every table in a database:

var dataModel = new ManagedDataModel.fromCurrentMirrorSystem(); dataModel.entities.forEach((e) { router .route("/${e.tableName}/[:id]") .generate(() => new ManagedObjectController.forEntity(e)); });