<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: KhoPhi</title>
    <description>The latest articles on DEV Community by KhoPhi (@khophi).</description>
    <link>https://dev.to/khophi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F19179%2F1e1b9707-51e4-4d3c-b978-d4d4eea9ddeb.jpeg</url>
      <title>DEV Community: KhoPhi</title>
      <link>https://dev.to/khophi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khophi"/>
    <language>en</language>
    <item>
      <title>Explain Declarative vs Imperative Programming like I'm 5</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Mon, 03 Feb 2020 12:07:56 +0000</pubDate>
      <link>https://dev.to/khophi/explain-declarative-vs-imperative-programming-like-i-m-5-2a1l</link>
      <guid>https://dev.to/khophi/explain-declarative-vs-imperative-programming-like-i-m-5-2a1l</guid>
      <description>&lt;p&gt;And perhaps, throw in why, as a 5-year-old, I will wanna use one over the other (if that makes any difference)&lt;/p&gt;

</description>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>Getting Started with the Aqueduct Framework</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Thu, 25 Apr 2019 13:55:53 +0000</pubDate>
      <link>https://dev.to/khophi/getting-started-with-the-aqueduct-framework-4abh</link>
      <guid>https://dev.to/khophi/getting-started-with-the-aqueduct-framework-4abh</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting an Aqueduct project&lt;/li&gt;
&lt;li&gt;Handling Migrations and providing seed data or fixtures for migrations&lt;/li&gt;
&lt;li&gt;Routing i.e handling params and query params&lt;/li&gt;
&lt;li&gt;Making CRUD requests, handled by a controller&lt;/li&gt;
&lt;li&gt;Using PostgreSQL as backend&lt;/li&gt;
&lt;li&gt;Basic pagination in Aqueduct&lt;/li&gt;
&lt;li&gt;Writing basic tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote class="wp-block-quote"&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;cite&gt;TL;DR&lt;br&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without much ado, let's rock and roll&lt;/p&gt;

&lt;h2&gt;Setting Up Project&lt;/h2&gt;

&lt;p&gt;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&lt;/p&gt;

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

&lt;h3 id="setting-up-database"&gt;Setting up Database&lt;/h3&gt;

&lt;p&gt;Create a postgreSQL database, call it anything you want.&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;  $ sudo -u postgres psql
  &amp;gt; CREATE USER projectuser WITH PASSWORD 'password';
  &amp;gt; CREATE DATABASE mydatabase;
  &amp;gt; GRANT ALL PRIVILEGES ON DATABASE mydatabase TO projectuser;
  &amp;gt; \q
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then create a &lt;code&gt;database.yaml&lt;/code&gt; file in the root project (same location as the &lt;code&gt;pubspec.yaml&lt;/code&gt;), and put in these:&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;username: 'projectuser'
password: 'password'
host: 'localhost'
port: 5432
databaseName: 'mydatabase'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above &lt;code&gt;database.yaml&lt;/code&gt; is for the &lt;code&gt;aqueduct db&lt;/code&gt;commands to make connections to the DB. However, for our application to speak to the database, we need update the &lt;code&gt;config.yaml&lt;/code&gt; with our database info.&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;database:
  host: 'localhost'
  port: 5432
  username: 'projectuser'
  password: 'password'
  databaseName: 'mydatabase'&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="migration"&gt; Migration&lt;/h3&gt;

&lt;blockquote class="wp-block-quote"&gt;
&lt;p&gt;If you're coming from somewhere like Django or Laravel, migration is likely a piece of cake for you. &lt;/p&gt;
&lt;p&gt;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&lt;/p&gt;
&lt;cite&gt;TL;DR&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;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 &lt;code&gt;migrations/00000001_initial.migration.dart&lt;/code&gt; file to see what is gonna happen.&lt;/p&gt;

&lt;p&gt;Also, seed/fixture data is added for free. Duh!&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;aqueduct db upgrade&lt;/code&gt; to commit the migration to database.&lt;/p&gt;

&lt;h3 id="run-application"&gt;Run Application&lt;/h3&gt;

&lt;p&gt;In the project root folder, run &lt;code&gt;aqueduct serve&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since this project is API endpoints only, you'll have to consume using a REST Client, such as &lt;a href="https://www.getpostman.com/"&gt;Postman&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;We've spent 2 seconds already. That's too much time. Let's actually get into the &lt;em&gt;real deals&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Application Channel, Entry Point &amp;amp; Routing&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;lib/channel.dart&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Nothing extraordinary happening. Just the same principles you know from your Ruby, &lt;a href="https://blog.khophi.co/category/django"&gt;DJango&lt;/a&gt; or Laravel. Heck, even &lt;a href="https://blog.khophi.co/category/javascript"&gt;Angular!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update your &lt;code&gt;lib/channel.dart&lt;/code&gt; file with these contents&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;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) =&amp;gt; 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(() =&amp;gt; ICDController(context));

    return router;
  }
}

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

  DatabaseConfiguration database;

  @optionalConfiguration
  int identifier;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In English:&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;router.route('/icd/[:id]').link(() =&amp;gt; ICDController(context));&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A lot is packed into the above single line of code. So much, I can write a book on this line alone. But well...&lt;/p&gt;

&lt;p&gt;The above route matches anything like this&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;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;amp;offset=x&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;[:id]&lt;/code&gt; is the params. The &lt;code&gt;[ ]&lt;/code&gt; around it makes that param optional. Thus, without an ID param, it'll do just fine. So if &lt;code&gt;example.com/icd&lt;/code&gt; comes in, the application will do just fine.&lt;/p&gt;

&lt;p&gt;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&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ICDController()&lt;/code&gt; was imported from the controller, which handles the routes coming through this endpoint.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So you see, just a single line, all the punch it throws? Excited yet? If not, the Controller might whet you up!&lt;/p&gt;

&lt;h2&gt;Mr. Controller&lt;/h2&gt;

&lt;p&gt;Our &lt;del&gt;Component&lt;/del&gt; 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.&lt;/p&gt;

&lt;p&gt;'Abeg', let's end the DB wars here!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In Aqueduct, we do so, by creating a &lt;code&gt;model/icd_model.dart&lt;/code&gt;&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;import 'package:mpharma/mpharma.dart';

class ICD extends ManagedObject&amp;lt;_ICD&amp;gt; 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;

}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The only part I'll comment on is&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;  @override
  void willInsert() {
    createdAt = DateTime.now().toUtc();
  }&lt;/code&gt;&lt;/pre&gt;

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

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

&lt;p&gt;The rest of the code is, well, code!&lt;/p&gt;

&lt;p&gt;I keep dangling the controller in your face, but not showing you the code? Here we go&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;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&amp;lt;Response&amp;gt; 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&amp;lt;ICD&amp;gt;(context);

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

    return Response.ok(icds);
  }

  @Operation.get('id')
  Future&amp;lt;Response&amp;gt; getById(@Bind.path('id') int id) async {
    final query = Query&amp;lt;ICD&amp;gt;(context)..where((icd) =&amp;gt; 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&amp;lt;Response&amp;gt; createICD(@Bind.body() ICD body) async {
    final query = Query&amp;lt;ICD&amp;gt;(context)..values = body;
    final insertICD = await query.insert();

    return Response.ok(insertICD);
  }

  @Operation.put('id')
  Future&amp;lt;Response&amp;gt; updateById(
      @Bind.path('id') int id, @Bind.body() ICD body) async {
    final query = Query&amp;lt;ICD&amp;gt;(context)
      ..where((icd) =&amp;gt; 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&amp;lt;Response&amp;gt; deleteById(@Bind.path('id') int id) async {
    final query = Query&amp;lt;ICD&amp;gt;(context)..where((icd) =&amp;gt; icd.id).equalTo(id);

    final icd = await query.delete();
    return Response.ok({'state': true, 'msg': 'Delete successfull'});
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A lot to unpack here? Nope. Just a few&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;@Operation.get()&lt;/code&gt;, &lt;code&gt;@Operation.post()&lt;/code&gt; etc, is more like the &lt;code&gt;router.get('/url', req, res)&lt;/code&gt; in Express&lt;/p&gt;

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

&lt;p&gt;Take note of this part &lt;strong&gt;first&lt;/strong&gt;,&lt;br&gt;&lt;code&gt;getAll({@Bind.query('limit') int limit, @Bind.query('offset') int pageby})&lt;/code&gt;&lt;br&gt;&lt;br&gt;When you do &lt;code&gt;getAll(@Bind.query...)&lt;/code&gt; it means totally different from &lt;code&gt;getAll({@Bind.query ...})&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Do you notice the difference? The second one is in curly braces. The first is not.&lt;/p&gt;

&lt;p&gt;Within curly braces mean it's &lt;strong&gt;&lt;a href="http://aqueduct.io/docs/http/resource_controller/#optional-bindings" rel="noreferrer noopener"&gt;optional parameters&lt;/a&gt;&lt;/strong&gt;, thus, in the above within the braces, if the URL doesn't supply any query params, we'll do just fine.&lt;/p&gt;

&lt;pre class="wp-block-code"&gt;&lt;code&gt;    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&amp;lt;ICD&amp;gt;(context);

    icdQuery
      ..pageBy((p) =&amp;gt; p.createdAt, QuerySortOrder.descending)
      ..fetchLimit = limit
      ..offset = pageby;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;limit ??= 100;&lt;/code&gt; is basically saying if the &lt;code&gt;limit&lt;/code&gt; query param is empty, just assign 100 to that var. Same for the &lt;code&gt;pageby&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;And so we're done. Source code to above project here:&lt;/p&gt;

&lt;p&gt;&lt;a rel="noreferrer noopener" href="https://github.com/seanmavley/aqueduct-crud"&gt;Aqueduct CRUD API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave any comments you might have in the comment section below.&lt;/p&gt;

&lt;p&gt;If you're into tests, &lt;a href="https://github.com/seanmavley/aqueduct-crud/tree/master/test" rel="noreferrer noopener"&gt;check the sample Test I wrote for this project.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aqueduct</category>
      <category>dart</category>
    </item>
    <item>
      <title>Do you tidy up your workspace when done coding?</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Thu, 28 Mar 2019 00:30:29 +0000</pubDate>
      <link>https://dev.to/khophi/do-you-tidy-up-your-workspace-when-done-coding-1d94</link>
      <guid>https://dev.to/khophi/do-you-tidy-up-your-workspace-when-done-coding-1d94</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3xzoqps3egzztongw4fx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3xzoqps3egzztongw4fx.gif" alt="Cleansing workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end of each coding session, I close all open files and collapse all sidebar menus. &lt;/p&gt;

&lt;p&gt;When I open up my code editor for the next coding session, I easily get confused and distracted with all those open files etc.&lt;/p&gt;

&lt;p&gt;'Cleansing' the workspace at the end of each coding session allows me to 'ramp' up to where I left off the previous time in style.&lt;/p&gt;

&lt;p&gt;However, in the middle of implementing a feature/fixing a bug, I only put computer in sleep mode.&lt;/p&gt;

&lt;p&gt;When done, with feature, pushes, CI builds, reflects on live, then the cleansing begins.&lt;/p&gt;

&lt;p&gt;Anyway, do you clean your workspace after each coding session, or you just slam the laptop lid shut and you're done?&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>editor</category>
    </item>
    <item>
      <title>Explain 3G, 4G and 5G like I'm 5</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Tue, 19 Mar 2019 17:36:21 +0000</pubDate>
      <link>https://dev.to/khophi/explain-3g-4g-and-5g-like-im-5-3i2c</link>
      <guid>https://dev.to/khophi/explain-3g-4g-and-5g-like-im-5-3i2c</guid>
      <description></description>
      <category>network</category>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>The fastest PWA blog experience I’ve always dreamed of</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Sat, 23 Jun 2018 14:01:54 +0000</pubDate>
      <link>https://dev.to/khophi/the-fastest-pwa-blog-experience-ive-always-dreamed-of-2n89</link>
      <guid>https://dev.to/khophi/the-fastest-pwa-blog-experience-ive-always-dreamed-of-2n89</guid>
      <description>&lt;p&gt;Progressive Web Apps (PWA) is the &lt;a href="https://www.urbandictionary.com/define.php?term=Distin" rel="noopener noreferrer"&gt;'distin'&lt;/a&gt; now. I had fun consuming my WordPress blog via API with a new UI, built as a PWA using Angular&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's a bonus feature on &lt;a href="https://www.khophi.blog" rel="noopener noreferrer"&gt;Khophi.Blog&lt;/a&gt;. Scroll down to try it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Earlier this year, I wanted a playground to have fun. My blog powered by WordPress has a boring UI (kinda), although it gets the job done.&lt;/p&gt;

&lt;p&gt;Aside the UI, the load time also was an issue for me. I stopped showing ads on the blog, with hopes it'll improve the load speeds.&lt;/p&gt;

&lt;p&gt;Well, that move did help, except not much.&lt;/p&gt;

&lt;p&gt;I could throw more firepower to my servers, and or do even more caching and other optimizations to get things going faster.&lt;/p&gt;

&lt;p&gt;Or, I could consume the WordPress via it's API. I went with this option, and &lt;a href="https://www.khophi.blog" rel="noopener noreferrer"&gt;Khophi.Blog&lt;/a&gt; was born.&lt;/p&gt;

&lt;h2&gt;
  
  
  PWAs - I might be obsessed. Help me!
&lt;/h2&gt;

&lt;p&gt;All I can think of for close to a year now is how best I can leverage the ever improving web technologies to build better, mobile-ready, and richer applications.&lt;/p&gt;

&lt;p&gt;So a PWA is basically, as defined by &lt;a href="https://ionicframework.com/pwa" rel="noopener noreferrer"&gt;Ionic&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"are a new way to offer incredible mobile app experiences that are highly optimized, reliable, and accessible completely on the web."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a website, a web application, but mobile users can at the same time experience the application as a mobile app on their phones.&lt;/p&gt;

&lt;p&gt;Angular is my go-to for building my PWAs.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Khophi.Blog
&lt;/h2&gt;

&lt;p&gt;I built Khophi.Blog to be as minimalistic as possible, with the utmost focus on speed. Thus, everything about the site is fast, lean, streamlined, and depending on who you ask, boring! And no ads.&lt;/p&gt;

&lt;p&gt;To achieve the above, &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular Framework&lt;/a&gt; was the choice of front-end framework for the UI plumbing. The grooming and fashion part is handled by &lt;a href="https://getuikit.com/" rel="noopener noreferrer"&gt;UI Kit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The backend is &lt;a href="https://blog.khophi.co/" rel="noopener noreferrer"&gt;blog.khophi.co&lt;/a&gt; (a WordPress CMS), which exposes API endpoints of which the frontend consumes.&lt;/p&gt;

&lt;p&gt;As you peruse the code, you should come across features making use of functionalities like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's PWA, installable on mobile and desktop&lt;/li&gt;
&lt;li&gt;Angular Animations&lt;/li&gt;
&lt;li&gt;The use of @Input and @Ouput for component interactions&lt;/li&gt;
&lt;li&gt;Handling offline mode&lt;/li&gt;
&lt;li&gt;Code reusability (via Components)&lt;/li&gt;
&lt;li&gt;HTTP Service&lt;/li&gt;
&lt;li&gt;Infinite scrolling&lt;/li&gt;
&lt;li&gt;Web Share API&lt;/li&gt;
&lt;li&gt;Caching content for offline viewing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;I'm constantly looking for ways to even make the application faster, and thinking of what I might need to remove to achieve that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It seems that perfection is attained not when there is nothing more to add, but when there is nothing more to remove" - Antoine de Saint Exupéry&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project is available on &lt;a href="https://bitbucket.org/seanmavley/blognext" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;, and PR and or &lt;a href="https://bitbucket.org/seanmavley/blognext/issues?status=new&amp;amp;status=open" rel="noopener noreferrer"&gt;Issues&lt;/a&gt; are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;You can now try your WordPress blog via Khophi.Blog.&lt;/p&gt;

&lt;p&gt;Steps are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit khophi.blog&lt;/li&gt;
&lt;li&gt;Tap/Click on the Hamburger menu&lt;/li&gt;
&lt;li&gt;Select "Try"&lt;/li&gt;
&lt;li&gt;Enter your WordPress site URL in there. Don't begin with https:// and don't end with anything after the domain. E.g theafricandream.net&lt;/li&gt;
&lt;li&gt;The blog will change to loading content from your WordPress blog. Depending on how complex you've rigged your WordPress site, some things might break. Happy to hear all the errors, if any.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply raise an issue and I'll take a look. If PR too, the better.&lt;/p&gt;

&lt;p&gt;See the GIF below to learn how.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.khophi.co%2Fwp-content%2Fuploads%2F2018%2F06%2FPeek-2018-06-14-14-44.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.khophi.co%2Fwp-content%2Fuploads%2F2018%2F06%2FPeek-2018-06-14-14-44.gif" alt="GIF of how trying Khophi.blog works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter your WordPress URL and start exploring your blog via KhoPhi Blog. I hope you enjoy the speed and ease PWAs bring.&lt;/p&gt;

&lt;p&gt;Someday, I might fully replace &lt;a href="//blog.khophi.co"&gt;blog.khophi.co&lt;/a&gt; with &lt;a href="https://www.khophi.blog" rel="noopener noreferrer"&gt;khophi.blog&lt;/a&gt; for good!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article was shared from &lt;a href="https://blog.khophi.co/khophi-blog-the-fastest-blog-experience-ive-always-dreamed-of/" rel="noopener noreferrer"&gt;Blog.Khophi.co&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>pwa</category>
      <category>wordpress</category>
      <category>api</category>
    </item>
    <item>
      <title>Google Duplex - The Conversation about Ethics?</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Thu, 10 May 2018 10:10:30 +0000</pubDate>
      <link>https://dev.to/khophi/google-duplex---the-conversation-about-ethics-2nce</link>
      <guid>https://dev.to/khophi/google-duplex---the-conversation-about-ethics-2nce</guid>
      <description>&lt;p&gt;In case no idea what Duplex is:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wqhxVwXI6q8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I for one, don't care, talking to a robot, whether I know or not, as long as it gets the job done, can convey the thoughts in a manner that is complete.&lt;/p&gt;

&lt;p&gt;Heck, we "talk" to Siri, Google Assistant and Alexa everyday.&lt;/p&gt;

&lt;p&gt;Google's Duplex is more like the other way round, where the Assistant rather initiates the conversation, and does it so well, it's human-like.&lt;/p&gt;

&lt;p&gt;Is it ethically wrong? What are the moral implications?&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-994028523563241472-521" src="https://platform.twitter.com/embed/Tweet.html?id=994028523563241472"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-994028523563241472-521');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=994028523563241472&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Should such robot's 'disclaim' themselves at the very beginning of such calls?&lt;/p&gt;

&lt;p&gt;Are you likely to pick a call by a bot if you know it's a bot, and the bot tells you, it's a bot?&lt;/p&gt;

&lt;p&gt;And just a fun scenario:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-994077948029362176-643" src="https://platform.twitter.com/embed/Tweet.html?id=994077948029362176"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-994077948029362176-643');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=994077948029362176&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>ai</category>
      <category>google</category>
    </item>
    <item>
      <title>Storing Image File (binary) vs Image Base64 in Database</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Mon, 30 Apr 2018 12:55:02 +0000</pubDate>
      <link>https://dev.to/khophi/storing-image-file-binary-vs-image-base64-in-database-26m3</link>
      <guid>https://dev.to/khophi/storing-image-file-binary-vs-image-base64-in-database-26m3</guid>
      <description>&lt;p&gt;Using frameworks like Django, the file is saved onto the harddrive, and a link pointing to the file is rather stored in the Database, which is fine.&lt;/p&gt;

&lt;p&gt;In my current API approach for uploading content, I submit the form, the part where there are images using the standard form data posting (the usual &lt;code&gt;multipart/form-data&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;So I simply cook up a payload that mimics the usual form-data submit using Angular, then push to an endpoint that accepts the form data submission.&lt;/p&gt;

&lt;p&gt;Recently, I'm thinking of going full base64 images.&lt;/p&gt;

&lt;p&gt;As in,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User selects image to upload&lt;/li&gt;
&lt;li&gt;Behind the scenes (on the browser), I encode the image to base64 string&lt;/li&gt;
&lt;li&gt;User submits form, then the usual JSON payload is sent with the image field as base64&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ data: 'string'
  image: 'base64 string'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've done the base64 image approach some time ago, and it was great (at least to me).&lt;/p&gt;

&lt;p&gt;Are there any performance issues with going with base64 image strings for storage in Database? Any caveats to look out for?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>discuss</category>
      <category>django</category>
    </item>
    <item>
      <title>Web Dev attempting Photography - Any Advice?</title>
      <dc:creator>KhoPhi</dc:creator>
      <pubDate>Tue, 27 Feb 2018 01:03:05 +0000</pubDate>
      <link>https://dev.to/khophi/web-dev-attempting-photography---any-advice--44nk</link>
      <guid>https://dev.to/khophi/web-dev-attempting-photography---any-advice--44nk</guid>
      <description>&lt;p&gt;I have always had an interest in photography. Now, I have the means to get a few beginner gadgets to start. I'm currently full time employed as a frontend web developer. However, I wish to take on photography as part time.&lt;/p&gt;

&lt;p&gt;Any tips on what I should keep in mind?&lt;/p&gt;

&lt;p&gt;Perhaps, someday, the photography endeavor might become my full time job, however until then, what steps can I take?&lt;/p&gt;

</description>
      <category>photography</category>
      <category>discuss</category>
      <category>offtopic</category>
    </item>
  </channel>
</rss>
