Getting Started with the Aqueduct Framework
Tags: aqueduct, dart
Published: 2019-04-25 13:55:53

<!-- wp:paragraph -->
<p>You ain't got time. I ain't got time neither. Let's get over with this pretty quick. Here are some of the facets of the Aqueduct framework you stand to understand in this guide.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Starting an Aqueduct project</li><li>Handling Migrations and providing seed data or fixtures for migrations</li><li>Routing i.e handling params and query params</li><li>Making CRUD requests, handled by a controller</li>
<li>Using PostgreSQL as backend</li><li>Basic pagination in Aqueduct</li><li>Writing basic tests.</li></ul>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>The reason behind the article (as well as many of my others) is the frustration I went through getting the most basic things done, and the unnecessary lengthy process other tutorials/articles go through, winding long and time wasting. For the full rant, see at the end.</p><cite>TL;DR<br></cite></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>Without much ado, let's rock and roll</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Setting Up Project</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>I'm not gonna pamper you. Here's how to setup an Aqueduct project. You don't have time, so just straight to the meat</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><a href="https://webdev.dartlang.org/tools/sdk#install">Install Dart</a></li><li><a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04">Install Postgres Database</a></li><li><a href="https://aqueduct.io/docs/getting_started/">Install Aqueduct</a></li><li>Clone the <a href="https://github.com/seanmavley/aqueduct-crud">Aqueduct-CRUD</a> Repository</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3 id="setting-up-database">Setting up Database</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Create a postgreSQL database, call it anything you want.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>  $ sudo -u postgres psql
  > CREATE USER projectuser WITH PASSWORD 'password';
  > CREATE DATABASE mydatabase;
  > GRANT ALL PRIVILEGES ON DATABASE mydatabase TO projectuser;
  > \q
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Then create a&nbsp;<code>database.yaml</code>&nbsp;file in the root project (same location as the&nbsp;<code>pubspec.yaml</code>), and put in these:</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>username: 'projectuser'
password: 'password'
host: 'localhost'
port: 5432
databaseName: 'mydatabase'</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The above <code>database.yaml</code> is for the <code>aqueduct db</code>commands to make connections to the DB. However, for our application to speak to the database, we need update the <code>config.yaml</code> with our database info.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>database:
  host: 'localhost'
  port: 5432
  username: 'projectuser'
  password: 'password'
  databaseName: 'mydatabase'</code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":3} -->
<h3 id="migration"> Migration</h3>
<!-- /wp:heading -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>If you're coming from somewhere like Django or Laravel, migration is likely a piece of cake for you. </p><p>Otherwise, think of migrations as a way to communicate your schema changes to the database, for real. If you didn't get that, please google for more details on migrations</p><cite>TL;DR</cite></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>Last but not least, run the migration, to convey what database scheme we have and want to the database for real. You can check the <code>migrations/00000001_initial.migration.dart</code>&nbsp;file to see what is gonna happen.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Also, seed/fixture data is added for free. Duh!</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Run&nbsp;<code>aqueduct db upgrade</code>&nbsp;to commit the migration to database.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="run-application">Run Application</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In the project root folder, run&nbsp;<code>aqueduct serve</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Since this project is API endpoints only, you'll have to consume using a REST Client, such as&nbsp;<a href="https://www.getpostman.com/">Postman</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If using Postman,&nbsp;<a href="https://www.getpostman.com/collections/b5c30941ce917a3a2ca9">check out this API request collection</a> to help you get started with consuming your endpoints. You might wanna read on how to use the collection in Postman.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We've spent 2 seconds already. That's too much time. Let's actually get into the <em>real deals</em></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Application Channel, Entry Point &amp; Routing</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In the <code>lib/channel.dart</code> is where we find, more or less, the bootstrapping of our application, and the few get-outta-ways we'll want done, such as, keeping in touch with the database by loading the config file, and also create one or two routes.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Nothing extraordinary happening. Just the same principles you know from your Ruby, <a href="https://blog.khophi.co/category/django">DJango</a> or Laravel. Heck, even <a href="https://blog.khophi.co/category/javascript">Angular!</a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Update your <code>lib/channel.dart</code> file with these contents</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>import 'controller/icd_controller.dart';
import 'dart:io';
import 'mpharma.dart';

class MpharmaChannel extends ApplicationChannel {
  ManagedContext context;

  @override
  Future prepare() async {
    logger.onRecord.listen(
        (rec) => print("$rec ${rec.error ?? ""} ${rec.stackTrace ?? ""}"));

                   // this Class is at the bottom
    final config = DatabaseConfig(options.configurationFilePath);

    final dataModel = ManagedDataModel.fromCurrentMirrorSystem();

    final persistentStore = PostgreSQLPersistentStore.fromConnectionInfo(
      config.database.username,
      config.database.password,
      config.database.host,
      config.database.port,
      config.database.databaseName,
    );

    context = ManagedContext(dataModel, persistentStore);
  }

  @override
  Controller get entryPoint {
    final router = Router();

    router.route("/example").linkFunction((request) async {
      return Response.ok({"key": "value"});
    });

    router.route('/icd/[:id]').link(() => ICDController(context));

    return router;
  }
}

class DatabaseConfig extends Configuration {
  DatabaseConfig(String path) : super.fromFile(File(path));

  DatabaseConfiguration database;

  @optionalConfiguration
  int identifier;
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>In English:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>We're importing a few. <code>dart:io</code> allows us to read files (we're reading the <code>config.yaml</code> file. Think of it as magic! </li><li>We also load a controller file. If coming from Angular, think of it as the Component.ts file</li><li>Then we create some routes. This part, lemme explain a bit</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><code>router.route('/icd/[:id]').link(() =&gt; ICDController(context));</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>A lot is packed into the above single line of code. So much, I can write a book on this line alone. But well...</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The above route matches anything like this</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>example.com/icd GET
example.com/icd POST
example.com/icd/1 GET
example.com/icd/1 PUT
example.com/icd/1 DELETE

example.com/icd?limit=x&amp;offset=x</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The <code>[:id]</code> is the params. The <code>[ ]</code> around it makes that param optional. Thus, without an ID param, it'll do just fine. So if <code>example.com/icd</code> comes in, the application will do just fine.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You'll see how each of these HTTP verbs are handled in the controller when we switch over to it next. However, know that we're using the HTTP verbs, as many as possible, at least to distinguish the requests</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <code>ICDController()</code> was imported from the controller, which handles the routes coming through this endpoint.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To pass query params, just pass it in the URL, like it's done in other frameworks. You only deal with the query params, if any, when in the controller.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>So you see, just a single line, all the punch it throws? Excited yet? If not, the Controller might whet you up!</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Mr. Controller</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Our <del>Component</del> Controller is where we handle the logic and communicates through the ORM that comes with Aqueduct to the Database. Above, we setup to use PostgreSQL, which depending on who you ask, is the best Relational Database platform in the world.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>'Abeg', let's end the DB wars here!</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Like other platforms, having a model for our database means we can more or less regulate what data structures flows through our application, and into the database.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In Aqueduct, we do so, by creating a <code>model/icd_model.dart</code></p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>import 'package:mpharma/mpharma.dart';

class ICD extends ManagedObject&lt;_ICD> implements _ICD {

  @override
  void willInsert() {
    createdAt = DateTime.now().toUtc();
  }
}

class _ICD {
  @primaryKey
  int id;

  @Column()
  String categoryCode;

  @Column()
  String diagnosisCode;

  @Column()
  String fullCode;

  @Column()
  String abbrDesc;

  @Column()
  String fullDesc;

  @Column()
  String categoryTitle;

  @Column()
  DateTime createdAt;

}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The only part I'll comment on is</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>  @override
  void willInsert() {
    createdAt = DateTime.now().toUtc();
  }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>In Mongoose (ExpressJS, NodeJS, MongoDB), there's the <code>{ timestamp: true }</code> option, which when specified, any data passing through the schema into the database, gets a <code>createdAt</code> and <code>updatedAt</code> timestamps. It happens for free, seamlessly</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In Aqueduct, a similar result can be achieved via the model signals. In the case above, whenever there's an insert about to happen - <code>willInsert()</code>, cook up a <code>createdAt</code> field, slap in there the current Datetime in UTC format, then add as a field to whatever data structure is about to be saved.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The rest of the code is, well, code!</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>I keep dangling the controller in your face, but not showing you the code? Here we go</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>import 'package:aqueduct/aqueduct.dart';
import 'package:mpharma/mpharma.dart';
import 'package:mpharma/model/icd_model.dart';

class ICDController extends ResourceController {
  ICDController(this.context);

  final ManagedContext context;

  @Operation.get()
  Future&lt;Response> getAll({@Bind.query('limit') int limit, @Bind.query('offset') int pageby}) async {

    limit ??= 100;
    pageby ??= 0;

    if (pageby != null) { pageby = pageby * 10; }
    
    final icdQuery = Query&lt;ICD>(context);

    icdQuery
      ..pageBy((p) => p.createdAt, QuerySortOrder.descending)
      ..fetchLimit = limit
      ..offset = pageby;
      
    final icds = await icdQuery.fetch();

    return Response.ok(icds);
  }

  @Operation.get('id')
  Future&lt;Response> getById(@Bind.path('id') int id) async {
    final query = Query&lt;ICD>(context)..where((icd) => icd.id).equalTo(id);

    final icd = await query.fetchOne();

    if (icd == null) {
      return Response.notFound(body: 'Not found');
    }

    return Response.ok(icd);
  }

  @Operation.post()
  Future&lt;Response> createICD(@Bind.body() ICD body) async {
    final query = Query&lt;ICD>(context)..values = body;
    final insertICD = await query.insert();

    return Response.ok(insertICD);
  }

  @Operation.put('id')
  Future&lt;Response> updateById(
      @Bind.path('id') int id, @Bind.body() ICD body) async {
    final query = Query&lt;ICD>(context)
      ..where((icd) => icd.id).equalTo(id)
      ..values = body;

    final icd = await query.updateOne();

    if (icd == null) {
      return Response.notFound(body: 'Not found');
    }

    return Response.ok(icd);
  }

  @Operation.delete('id')
  Future&lt;Response> deleteById(@Bind.path('id') int id) async {
    final query = Query&lt;ICD>(context)..where((icd) => icd.id).equalTo(id);

    final icd = await query.delete();
    return Response.ok({'state': true, 'msg': 'Delete successfull'});
  }
}
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>A lot to unpack here? Nope. Just a few</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>  @Operation.get()
  Future&lt;Response> getAll({@Bind.query('limit') int limit, @Bind.query('offset') int pageby}) async {
    print(limit);
    limit ??= 100;
    pageby ??= 0;</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p><code>@Operation.get()</code>, <code>@Operation.post()</code> etc, is more like the <code>router.get('/url', req, res)</code> in Express</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>Future&lt;Response> getAll({@Bind.query('limit') int limit, @Bind.query('offset') int pageby}) async { .. }</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Take note of this part <strong>first</strong>,<br><code>getAll({@Bind.query('limit') int limit, @Bind.query('offset') int pageby})</code><br><br>When you do <code>getAll(@Bind.query...)</code> it means totally different from <code>getAll({@Bind.query ...})</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Do you notice the difference? The second one is in curly braces. The first is not.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Within curly braces mean it's <strong><a href="http://aqueduct.io/docs/http/resource_controller/#optional-bindings" target="_blank" rel="noreferrer noopener" aria-label="optional parameters (opens in a new tab)">optional parameters</a></strong>, thus, in the above within the braces, if the URL doesn't supply any query params, we'll do just fine.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>    limit ??= 100;
    pageby ??= 0;

    // should this next line even be there? I wrote it
    // Can't tell for sure if it should be there. ¯\_(ツ)_/¯
    if (pageby != null) { pageby = pageby * 10; }
    
    final icdQuery = Query&lt;ICD>(context);

    icdQuery
      ..pageBy((p) => p.createdAt, QuerySortOrder.descending)
      ..fetchLimit = limit
      ..offset = pageby;</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p><code>limit ??= 100;</code> is basically saying if the <code>limit</code> query param is empty, just assign 100 to that var. Same for the <code>pageby</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The rest, is just straight code written in English. If any part of the controller code isn't clear, kindly leave a comment. Otherwise I take it as y'all readers (more like my classroom students) said "Yes sir, we understand" to me.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Conclusion</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>And so we're done. Source code to above project here:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" aria-label="Aqueduct CRUD API (opens in a new tab)" href="https://github.com/seanmavley/aqueduct-crud" target="_blank">Aqueduct CRUD API</a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Leave any comments you might have in the comment section below.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If you're into tests, <a href="https://github.com/seanmavley/aqueduct-crud/tree/master/test" target="_blank" rel="noreferrer noopener" aria-label="check the sample Test I wrote for this project. (opens in a new tab)">check the sample Test I wrote for this project.</a></p>
<!-- /wp:paragraph -->