<?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: Aman Virk</title>
    <description>The latest articles on DEV Community by Aman Virk (@amanvirk1).</description>
    <link>https://dev.to/amanvirk1</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%2F459643%2Fa60841f4-b4b4-46d6-bacb-3d69999323a7.jpg</url>
      <title>DEV Community: Aman Virk</title>
      <link>https://dev.to/amanvirk1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amanvirk1"/>
    <language>en</language>
    <item>
      <title>Learn how to generate unique post slugs in Node.js</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Mon, 28 Jun 2021 08:31:30 +0000</pubDate>
      <link>https://dev.to/amanvirk1/adonisjs-generating-unique-slugs-fmj</link>
      <guid>https://dev.to/amanvirk1/adonisjs-generating-unique-slugs-fmj</guid>
      <description>&lt;p&gt;I recently released a package called &lt;a href="https://github.com/adonisjs/lucid-slugify"&gt;lucid slugify&lt;/a&gt; which helps you generate unique slugs from your Lucid models.&lt;/p&gt;

&lt;p&gt;In this video, I share the reasoning behind creating this package and also how you can use the package in your AdonisJS apps.&lt;/p&gt;

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

</description>
      <category>node</category>
      <category>adonisjs</category>
    </item>
    <item>
      <title>Creating responsive emails using MJML and AdonisJS</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Mon, 14 Dec 2020 08:27:48 +0000</pubDate>
      <link>https://dev.to/amanvirk1/creating-responsive-emails-using-mjml-and-adonisjs-21n5</link>
      <guid>https://dev.to/amanvirk1/creating-responsive-emails-using-mjml-and-adonisjs-21n5</guid>
      <description>&lt;p&gt;I have been using &lt;a href="https://mjml.io/"&gt;MJML&lt;/a&gt; for a while to design the email templates for my projects.&lt;/p&gt;

&lt;p&gt;In the following video I share my process of using it along side &lt;a href="https://preview.adonisjs.com"&gt;AdonisJS&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>adonisjs</category>
      <category>node</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Database setup</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Tue, 24 Nov 2020 07:19:09 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-database-setup-3936</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-database-setup-3936</guid>
      <description>&lt;p&gt;Let's talk about data. In this post, we will set up Lucid (AdonisJS SQL ORM) and make use of the migrations to create the necessary database tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;This article assumes that you already have some SQL database server setup on your computer and you know how to create a database manually.&lt;/p&gt;

&lt;p&gt;If this is not the case, then I recommend using the &lt;code&gt;sqlite&lt;/code&gt; database during the setup process. SQLite is a file-based database and doesn't anything else other than the &lt;a href="http://npmjs.com/package/sqlite3" rel="noopener noreferrer"&gt;sqlite3 npm package&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lucid setup
&lt;/h2&gt;

&lt;p&gt;Like always, the first step is to install the package from npm by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @adonisjs/lucid@alpha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done, run the following command to set up the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace invoke @adonisjs/lucid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ace invoke&lt;/code&gt; command executes the &lt;a href="https://github.com/adonisjs/lucid/blob/develop/instructions.ts" rel="noopener noreferrer"&gt;instructions Javascript&lt;/a&gt; file exposed by the package.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1606191426%2Fdevto%2Fsetup-lucid_ps3jgu.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1606191426%2Fdevto%2Fsetup-lucid_ps3jgu.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I decided to go with the PostgreSQL database. You can choose any of the available databases.&lt;/li&gt;
&lt;li&gt;Right after the setup command is completed, we need to copy the code for validating the environment variables to the &lt;code&gt;env.ts&lt;/code&gt; file.
Since environment variables are injected from the outside, AdonisJS recommends you validate them and ensure that your app is always running with the correct set of configuration values.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;config/database.ts&lt;/code&gt; file holds all the configuration related to the database.&lt;/li&gt;
&lt;li&gt;Finally, the config file relies on the environment variables and you can update them inside the &lt;code&gt;.env&lt;/code&gt; file. My values are
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;PG_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
  &lt;span class="nv"&gt;PG_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5432
  &lt;span class="nv"&gt;PG_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;virk
  &lt;span class="nv"&gt;PG_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;PG_DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;todo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Overview
&lt;/h2&gt;

&lt;p&gt;Before we start writing any code, let me give you a brief overview of the AdonisJS data layer. We ship with an &lt;a href="https://preview.adonisjs.com/guides/database/introduction" rel="noopener noreferrer"&gt;in-house ORM (Lucid)&lt;/a&gt; to work with the SQL databases.&lt;/p&gt;

&lt;p&gt;Along with the support for all major SQL databases, the following are some of the hand-picked features of Lucid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Active record ORM&lt;/strong&gt;: The data models of Lucid follows the &lt;a href="https://en.wikipedia.org/wiki/Active_record_pattern" rel="noopener noreferrer"&gt;Active record pattern&lt;/a&gt; to simplify the database interactions. All the base level features including CRUD operations, relationships, serializing models to JSON are covered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database query builder&lt;/strong&gt;: The database query builder is built on top of &lt;a href="http://knexjs.org" rel="noopener noreferrer"&gt;knex&lt;/a&gt; and allows you to construct SQL queries using the Javascript methods.&lt;br&gt;
The database query builder is one level down from the data models, meaning the queries you write are sent as it is to the database driver without any modifications. This is usually helpful when you are writing complex queries that model high-level API cannot construct for you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema migrations&lt;/strong&gt;: Migrations allow you to create and evolve your database schema using code. It also comes with an inbuilt tracking system, so that every migration file is executed only once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model factories&lt;/strong&gt;: &lt;a href="https://preview.adonisjs.com/guides/database/factories" rel="noopener noreferrer"&gt;Factories&lt;/a&gt; allows you to create/persist data models using fake data. They are usually helpful when writing tests or when seeding a database with some initial set of records.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Seeders&lt;/strong&gt;: Database seeders can be used with or without factories to seed the database. For example, I will use factories to seed random data during development and may have a couple of seeders with real data like &lt;strong&gt;list of countries&lt;/strong&gt; that should be in the database before deploying the app.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Schema migrations
&lt;/h2&gt;

&lt;p&gt;As covered in the overview section the schema migrations allow us to create the database schema using code. So let's create the tables we need for our todo app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the categories table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:migration categories

&lt;span class="c"&gt;# CREATE: database/migrations/1606195827305_categories.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created file and paste the following code snippet inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseSchema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Categories&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseSchema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;categories&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The migration file has two methods. The &lt;code&gt;up&lt;/code&gt; method is used to perform an action that is executed during the &lt;code&gt;node ace migration:run&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;down&lt;/code&gt; method should undo the action taken inside the &lt;code&gt;up&lt;/code&gt; method. If the up method creates a table, then the down method should drop the table.&lt;/li&gt;
&lt;li&gt;In the above migration file we are creating the &lt;code&gt;categories&lt;/code&gt; table with a total of four columns

&lt;ul&gt;
&lt;li&gt;The auto-increment &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The category &lt;code&gt;name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The category &lt;code&gt;slug&lt;/code&gt;. It is the URL friendly version of the category name&lt;/li&gt;
&lt;li&gt;And finally a color code. This is just to enhance the UI.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's run this migration by executing the following ace command. Also, make sure that you have manually created the database first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace migration:run

&lt;span class="c"&gt;# migrated database/migrations/1606195827305_categories&lt;/span&gt;
&lt;span class="c"&gt;# Migrated in 173 ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you re-run the same command, you will get the following message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace migration:run

&lt;span class="c"&gt;# Already up to date&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because Lucid tracks the migration files and do not re-run them. However, during development you can &lt;a href="https://preview.adonisjs.com/guides/database/migrations#changing-existing-migrations" rel="noopener noreferrer"&gt;rollback your changes&lt;/a&gt; using the following ace command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace migration:rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the todos table
&lt;/h3&gt;

&lt;p&gt;Let's repeat the same process for creating the todos table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:migration todos

&lt;span class="c"&gt;# CREATE: database/migrations/1606197725778_todos.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created file and paste the following code inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseSchema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Todos&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseSchema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;category_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;categories&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is_completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;defaultTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the todos migration file, we define a foreign key constraint on the &lt;code&gt;category_id&lt;/code&gt; at the database level.&lt;/p&gt;

&lt;p&gt;Finally, run the following command to create the todos table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace migration:run

&lt;span class="c"&gt;# migrated database/migrations/1606197725778_todos&lt;/span&gt;
&lt;span class="c"&gt;# Migrated in 159 ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data models
&lt;/h2&gt;

&lt;p&gt;Data models are ES6 classes, where each class is responsible for interacting with a single database table. For our application, we will need two data models - one for the &lt;code&gt;todos&lt;/code&gt; table and another one for the &lt;code&gt;categories&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;Also, I want to mention - &lt;strong&gt;Models and migrations are not inter-connected, they are two separate tools that performs different tasks. You can have data models without migrations and vice-versa.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the &lt;code&gt;Category&lt;/code&gt; model
&lt;/h3&gt;

&lt;p&gt;Run the following command to create a model for the &lt;code&gt;categories&lt;/code&gt; table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:model Category

&lt;span class="c"&gt;# CREATE: app/Models/Category.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created file and paste the following code inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Orm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;isPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;colorCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;autoUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first thing is you have to define all the table columns on your models and apply the &lt;code&gt;@column&lt;/code&gt; decorator to them. This helps Lucid distinguish between the standard model properties and the table columns.&lt;/li&gt;
&lt;li&gt;The data types next to the column name are the Typescript data types. We are not dealing with database types like &lt;code&gt;varchar&lt;/code&gt; here. For Typescript, there is nothing called varchar.&lt;/li&gt;
&lt;li&gt;Finally, we allow you to use &lt;strong&gt;camelCase property names&lt;/strong&gt; inside your models and during the SQL queries, we will convert them to &lt;strong&gt;snake_case&lt;/strong&gt;. For example: The &lt;code&gt;colorCode&lt;/code&gt; will become &lt;code&gt;color_code&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating the todos model
&lt;/h3&gt;

&lt;p&gt;Let's create the model for the &lt;code&gt;todos&lt;/code&gt; table and set up the relationship between the &lt;code&gt;Todo&lt;/code&gt; and the &lt;code&gt;Category&lt;/code&gt; models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:model Todo

&lt;span class="c"&gt;# CREATE: app/Models/Todo.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Orm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Todo&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;isPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;categoryId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;autoUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up the relationship
&lt;/h3&gt;

&lt;p&gt;Following are the relationships between the todos and the categories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Categories has many todos.&lt;/li&gt;
&lt;li&gt;A todo belongs to a category.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't have to define the relationship on both ends. Simply define the one's that you will be using inside your app. For example, We will be using the &lt;code&gt;Category&lt;/code&gt; model to fetch the related todos, and therefore we will only setup the &lt;code&gt;hasMany&lt;/code&gt; relationship.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app/Models/Category.ts&lt;/code&gt; file and add the following import statements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HasMany&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Orm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Models/Todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, define the relationship as a property on the class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ....&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HasMany&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo time
&lt;/h2&gt;

&lt;p&gt;With everything in place. Let's fire up the AdonisJS repl and run a few queries using our models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace repl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid too much typing, you can copy/paste the following code snippets inside the REPL session.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Load models by running the following method.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;loadModels&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create personal category&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;personal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Personal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;personal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;colorCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4fd1c5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create work category&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;colorCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e53e3e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add todo inside the work category&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kick start "Introducing AdonisJS" articles series on dev.to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add todo inside the personal category&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;personal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Buy groceries when going back to home. Make sure to buy some chocolates too&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1606200657%2Fdevto%2Frepl_u3wmxx.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1606200657%2Fdevto%2Frepl_u3wmxx.gif"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;You can open the database inside some GUI tool and verify that the rows are added to the respective database tables with correct foreign keys.&lt;/p&gt;

</description>
      <category>node</category>
      <category>adonisjs</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Designing the web page</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Fri, 20 Nov 2020 07:42:55 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-designing-the-web-page-a8d</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-designing-the-web-page-a8d</guid>
      <description>&lt;p&gt;Alright, let's begin designing the by web page for listing all of our todos, along with an input box to create a new one. In this post, you will learn about Edge &lt;strong&gt;layouts&lt;/strong&gt; and &lt;strong&gt;components&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layouts
&lt;/h2&gt;

&lt;p&gt;Layouts are standard edge templates that expose the sections in which the children templates can inject their markup. Let's see them in practice.&lt;/p&gt;

&lt;p&gt;Run the following ace command to create a new template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:view layouts/master

&lt;span class="c"&gt;# CREATE: resources/views/layouts/master.edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;master.edge&lt;/code&gt; file is created inside the &lt;code&gt;layouts&lt;/code&gt; directory. Again, this is just a convention and not a technical limitation to keep the layouts inside this directory.&lt;/p&gt;

&lt;p&gt;Open the newly created file and paste the following code snippet inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-screen font-sans"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-lg m-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"99"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"33"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M57.19 13.304h-3.653v12.28h-3.063v-12.28H48v-2.77h2.474V8.55c0-1.322.422-2.401 1.266-3.238.864-.855 1.974-1.283 3.328-1.283.786 0 1.434.146 1.944.438.53.291.913.573 1.149.846l-1.03 2.304a2.92 2.92 0 00-.767-.525 1.922 1.922 0 00-.942-.263c-.707 0-1.198.165-1.473.496-.275.33-.412.739-.412 1.225v1.983h3.652v2.771zm3.18 12.28V4h3.034v21.583H60.37zM80.657 10.27v15.313h-3.063v-2.362c-.471.642-1.168 1.244-2.091 1.808-.923.564-1.993.846-3.21.846a6.552 6.552 0 01-3.564-1.02c-1.06-.681-1.905-1.624-2.533-2.83-.628-1.206-.943-2.586-.943-4.142 0-1.555.324-2.926.972-4.112.648-1.206 1.542-2.14 2.68-2.8 1.14-.68 2.435-1.021 3.888-1.021 1.08 0 2.022.233 2.828.7.824.467 1.482 1.011 1.973 1.633v-2.012h3.063zm-7.658 12.834c.943 0 1.767-.223 2.474-.67a4.768 4.768 0 001.65-1.867c.392-.798.589-1.692.589-2.684 0-.972-.197-1.847-.59-2.625a4.557 4.557 0 00-1.649-1.837c-.707-.467-1.531-.7-2.474-.7-.903 0-1.708.223-2.415.67a4.555 4.555 0 00-1.65 1.838c-.392.778-.588 1.663-.588 2.654 0 .992.196 1.886.589 2.684.393.777.942 1.4 1.65 1.866.706.448 1.511.671 2.414.671zM91.784 10.008c1.375 0 2.602.34 3.682 1.021 1.1.661 1.963 1.585 2.592 2.771.628 1.186.942 2.557.942 4.113 0 1.536-.314 2.906-.942 4.112-.629 1.186-1.483 2.12-2.563 2.8-1.08.68-2.287 1.02-3.622 1.02-1.1 0-2.101-.232-3.005-.7-.883-.466-1.58-1-2.09-1.603V32h-3.064V10.242h3.063v2.245c.452-.641 1.13-1.215 2.033-1.72a5.986 5.986 0 012.974-.759zm-.412 2.771c-.923 0-1.748.224-2.474.671a4.77 4.77 0 00-1.679 1.838c-.392.758-.589 1.633-.589 2.625 0 .972.197 1.847.59 2.625a5 5 0 001.678 1.866c.726.447 1.551.671 2.474.671.923 0 1.738-.224 2.444-.67a4.767 4.767 0 001.65-1.867c.412-.778.618-1.653.618-2.625 0-.992-.206-1.867-.618-2.626a4.554 4.554 0 00-1.65-1.837c-.706-.447-1.521-.67-2.444-.67z"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M7.832.904a3.949 3.949 0 00-3.591 3L.923 17.422v11.226a3.948 3.948 0 003.948 3.949h27.734a3.948 3.948 0 003.949-3.949V16.926L33.456 3.93a3.948 3.948 0 00-3.84-3.033h-.068v3.948h.068l2.99 12.545v11.258H4.87V17.9L7.832 5.835V.905z"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#5A4FD6"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M18.69 24.587a5.925 5.925 0 005.586-3.948H34.78V16.69H20.664v1.974a1.974 1.974 0 01-3.948 0V16.69H2.778v3.949h10.327a5.925 5.925 0 005.585 3.948zM28.561 8.793H8.82v3.949h19.742V8.793zM26.587 2.87H10.794v3.95h15.793V2.87z"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#5A4FD6"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600 text-xl font-normal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://dev.to/amanvirk1/introducing-adonisjs-28af"&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-900 underline"&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Follow along&lt;span class="nt"&gt;&amp;lt;/a&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          as we build this todo app using AdonisJS, TailwindCSS and Alpine.js.
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

      @!section('body')
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The keywords starting with &lt;code&gt;@&lt;/code&gt; are called tags in Edge. For example The &lt;code&gt;@layout&lt;/code&gt; tag, the &lt;code&gt;@section&lt;/code&gt; tag, and so on.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;master.edge&lt;/code&gt; file defines the markup that will remain the same on all the pages.&lt;/li&gt;
&lt;li&gt;Also it creates an empty section &lt;code&gt;@!section('body')&lt;/code&gt;, that children templates can inject their markup into and that will be placed right after the header.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To give it a try, let's open the &lt;code&gt;todos/index.edge&lt;/code&gt; file we created in the last post and replace all of its content with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@layout('layouts/master')

@section('body')
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Todos&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
@endsection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;@layout&lt;/code&gt; tag, we tell edge about the layout we want to use.&lt;/li&gt;
&lt;li&gt;Next, we use the &lt;code&gt;@section&lt;/code&gt; tag to inject our markup. The section names needs to be the same inside this file and the layout file.&lt;/li&gt;
&lt;li&gt;Anything outside the section tag will be ignored as Edge doesn't know where to place that markup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start the development server by running &lt;code&gt;node ace serve --watch&lt;/code&gt; and &lt;code&gt;node ace mix:watch&lt;/code&gt; commands and view the webpage in the browser.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605851716%2Fdevto%2Fwebpage-first-look.png" 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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605851716%2Fdevto%2Fwebpage-first-look.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tweaking styles
&lt;/h2&gt;

&lt;p&gt;Great. Let's take a step further and update the tailwindcss config to define our own set of colors and shadows. Open the &lt;code&gt;tailwind.config.js&lt;/code&gt; file and replace its contents with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./resources/views/**/*.edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transparent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;white&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#24272e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#64697e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#7d849c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#d2d5df&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#eff0f4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px 1px 14px rgba(0,0,0,0.08)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 1px 2px 0 rgba(0, 0, 0, 0.05)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.125rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;sans&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nunito Sans&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus-within&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus-within&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I have defined my own set of &lt;strong&gt;colors&lt;/strong&gt; and &lt;strong&gt;box shadows&lt;/strong&gt;. If you notice, they are outside of the &lt;code&gt;extend&lt;/code&gt; object. It means, we are not extending the tailwind config, instead are completely overwriting it.&lt;/li&gt;
&lt;li&gt;Next, I extend the margin utilities to have a &lt;code&gt;m-0.5&lt;/code&gt; class and use &lt;code&gt;Nunito Sans&lt;/code&gt; as the main font family.&lt;/li&gt;
&lt;li&gt;I want tailwindcss to generate box-shadow and background color classes for &lt;code&gt;hover&lt;/code&gt;, &lt;code&gt;focus&lt;/code&gt;, and &lt;code&gt;focus-within&lt;/code&gt; states.&lt;/li&gt;
&lt;li&gt;Finally, we want the purgeCSS to scan our edge templates and remove the unused CSS. You can learn more about it in the &lt;a href="https://tailwindcss.com/docs/optimizing-for-production" rel="noopener noreferrer"&gt;tailwindcss docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's import the Nunito Sans font from Google fonts. Open the &lt;code&gt;resources/css/styles.css&lt;/code&gt; file and add the following file of code to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,wght@0,300;0,400;1,600&amp;amp;display=swap')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, I want to give the body a gradient background, so let's add that too in the same file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-webkit-linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#e2e2e2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#e2e2e2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;To begin with, do not confuse Edge components with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer"&gt;Web components&lt;/a&gt; or even with React or Vue components.&lt;/p&gt;

&lt;p&gt;Components in Edge means a piece of a template that has its isolated state from the rest of the template. However, they do allow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passing props to them&lt;/li&gt;
&lt;li&gt;Defining slots to inject markup to the component body&lt;/li&gt;
&lt;li&gt;And a component is capable of sharing data with the component caller&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do remember all this is happening on the server-side. Edge is &lt;strong&gt;not a frontend template engine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will be creating a total of three different components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A component to render the markup for a single todo.&lt;/li&gt;
&lt;li&gt;A component to display the input box for creating a new todo.&lt;/li&gt;
&lt;li&gt;Finally, one for the dropdown to filter todos by their list. This one will also use Alpine.js to show/hide the dropdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dropdown component
&lt;/h3&gt;

&lt;p&gt;Let's begin with the dropdown component first. Run the following command to create a new template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:view components/dropdown

&lt;span class="c"&gt;# CREATE: resources/views/components/dropdown.edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created file and paste the following code snippet inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-200 rounded py-2 px-4 focus-within:bg-white focus-within:shadow relative"&lt;/span&gt;
  &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ opened: false }"&lt;/span&gt;
  &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.away=&lt;/span&gt;&lt;span class="s"&gt;"opened = false"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"opened = !opened"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ items.find((item) =&amp;gt; item.id === selected).text }}
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-3 h-3"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M19 9l-7 7-7-7"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white shadow absolute whitespace-no-wrap left-0 rounded py-3 mt-1 w-40"&lt;/span&gt;
    &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"opened"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @each(item in items)
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
        &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ item.url }}"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-700 flex py-1 items-center px-4 hover:bg-gray-100"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-3 h-3 rounded mr-2 border-2"&lt;/span&gt;
          &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"border-color: {{ item.color }}"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        {{ item.text }}
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    @endeach
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We have a standard markup with tailwind classes to style the elements.&lt;/li&gt;
&lt;li&gt;The component needs an array of &lt;code&gt;items&lt;/code&gt; for the dropdown list. Every item inside the array is an object with the following properties.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: A unique identifier for the item&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt;: The display text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt;: Just for beautifying the list&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt;: The URL to visit on click.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Finally, it needs a &lt;code&gt;selected&lt;/code&gt; prop to know the selected item from the list.&lt;/li&gt;

&lt;li&gt;Also we have added the alpine attributes to make the component functional. If you are new to alpine, then I recommend spending some time reading the &lt;a href="https://github.com/alpinejs/alpine" rel="noopener noreferrer"&gt;README file&lt;/a&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's use this component inside the &lt;code&gt;todos/index.edge&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@section('body')
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-between"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      @!component('components/dropdown', {
        selected: 'all',
        items: [
          {
            id: 'all',
            url: '?list=all',
            text: 'All',
            color: '#999',
          },
          {
            id: 'personal',
            url: '?list=personal',
            text: 'Personal',
            color: '#4fd1c5',
          },
          {
            id: 'work',
            url: '?list=work',
            text: 'Work',
            color: '#e53e3e',
          },
        ]
      })
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
@endsection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@component&lt;/code&gt; tag accepts the path to the component template, along with a props object.&lt;/li&gt;
&lt;li&gt;For now, we are hardcoding the list items. Later we will get it from the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Demo time 😎&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605855381%2Fdevto%2Fdropdown_lihpbw.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605855381%2Fdevto%2Fdropdown_lihpbw.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Input component
&lt;/h3&gt;

&lt;p&gt;Let's repeat the same process and create another component for the input box to create a new todo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:view components/input

&lt;span class="c"&gt;# CREATE: resources/views/components/input.edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-200 rounded py-2 px-6 focus-within:bg-white focus-within:shadow flex-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"{{ name }}"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"{{ name }}"&lt;/span&gt;
    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ flashMessages.get(name) || value || '' }}"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"{{ placeholder }}"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-transparent w-full outline-none placeholder-gray-600"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  @if(flashMessages.has(`errors.${name}`))
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ flashMessages.get(`errors.${name}`) }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  @endif
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The input component needs the input &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;placeholder&lt;/code&gt;, and an optional &lt;code&gt;value&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ignore all the code related to &lt;code&gt;flashMessages&lt;/code&gt; for now. I have added them to make the component future-ready and we will learn about flash messages later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's also use this component inside the &lt;code&gt;todos/index.edge&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;@!component('components/dropdown', {... })&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/todos"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @!component('components/input', {
      name: 'text',
      placeholder: 'Start typing to create a new todo',
    })
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Todo component
&lt;/h3&gt;

&lt;p&gt;Let's wrap up by creating a component to show a single todo item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:view components/todo

&lt;span class="c"&gt;# CREATE: resources/views/components/todo.edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex text-gray-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-0.5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"is_completed"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block form-checkbox"&lt;/span&gt;
      &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;''&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-3 leading-tight flex-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ text }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-0.5 text-gray-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-4 h-4"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This time we just need the todo text and whether it's completed or not.&lt;/li&gt;
&lt;li&gt;Later we will introduce some forms inside this component to mark the todo as done or to delete it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's update the &lt;code&gt;todos/index.edge&lt;/code&gt; file and use this component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded-lg shadow py-3 px-4 mt-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @!component('components/todo', {
      text: 'Kick start "Introducing AdonisJS" articles series on dev.to',
      isCompleted: true,
    })
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded-lg shadow py-3 px-4 mt-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @!component('components/todo', {
      text: 'Buy groceries when going back to home. Make sure to buy some chocolates too',
      isCompleted: false,
    })
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded-lg shadow py-3 px-4 mt-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @!component('components/todo', {
      text: 'Record podcast. Ping Romain for same',
      isCompleted: true,
    })
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tailwindcss</category>
      <category>node</category>
      <category>adonisjs</category>
      <category>alpinejs</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Setup up Laravel mix</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Tue, 17 Nov 2020 07:36:13 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-setup-laravel-mix-3660</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-setup-laravel-mix-3660</guid>
      <description>&lt;p&gt;In this post, we will learn how to use the AdonisJS template engine (Edge) and set up Webpack/Laravel mix to process TailwindCSS and Alpine.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Views &amp;amp; Templates
&lt;/h2&gt;

&lt;p&gt;The official and the recommended template engine of AdonisJS is Edge. It is a logical template engine and comes with some neat features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An easy to write syntax&lt;/li&gt;
&lt;li&gt;Support for conditionals, loops, layouts, and partials&lt;/li&gt;
&lt;li&gt;Support for components &lt;strong&gt;(a personal favorite)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Allows runtime debugging using Chrome DevTools&lt;/li&gt;
&lt;li&gt;Accurate stack traces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We won't be covering every feature of the edge in this series and just focus on the pieces we need for our todo app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rendering views
&lt;/h3&gt;

&lt;p&gt;Open the &lt;code&gt;start/routes.ts&lt;/code&gt; file and replace all of its contents with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TodosController.index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming you already have the &lt;code&gt;TodosController&lt;/code&gt; from the previous post. Replace its contents with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/HttpContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodosController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;view.render&lt;/code&gt; methods takes the path to the template file stored inside the &lt;code&gt;resources/views&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Adding a file extension is optional.&lt;/li&gt;
&lt;li&gt;The return value is a string. In our case, it will be a string containing the final HTML.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also render views using the &lt;code&gt;Adonis/Core/View&lt;/code&gt; module directly. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/View&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there is a difference between using the module directly and using the &lt;code&gt;ctx.view&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ctx.view&lt;/code&gt; object also contains the information about the current HTTP request like &lt;strong&gt;the request details&lt;/strong&gt;, &lt;strong&gt;authenticated user&lt;/strong&gt;, &lt;strong&gt;session flash messages&lt;/strong&gt; and so on. Therefore it is recommended to always use &lt;code&gt;ctx.view&lt;/code&gt; during an HTTP request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the view file
&lt;/h3&gt;

&lt;p&gt;Let's create the &lt;code&gt;todos/index.edge&lt;/code&gt; file using the following ace command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:view todos/index

&lt;span class="c"&gt;# CREATE: resources/views/todos/index.edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created file and paste the following contents inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Todos app&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;We will being rendering todos here&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, visit the &lt;a href="http://localhost:3333" rel="noopener noreferrer"&gt;http://localhost:3333&lt;/a&gt; URL to view the rendered HTML.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605591018%2Fdevto%2Frender-views_cpxna2.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605591018%2Fdevto%2Frender-views_cpxna2.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Serving static assets
&lt;/h2&gt;

&lt;p&gt;Everything we have done so far is on the server-side. However, we do need some way to style our webpages using CSS and also write front-end JavaScript to make the pages interactive.&lt;/p&gt;

&lt;p&gt;Let's &lt;strong&gt;start with the basics&lt;/strong&gt; and slowly move towards using a build tool like &lt;a href="https://webpack.js.org" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; to bundle the front-end assets.&lt;/p&gt;

&lt;p&gt;To begin with, we need some way to serve CSS and JavaScript files to the browser. In AdonisJS, you need to keep these files inside the &lt;code&gt;public&lt;/code&gt; folder and then access them using the relative path. Let's give it a try.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;styles.css&lt;/code&gt; inside the &lt;code&gt;public&lt;/code&gt; directory and paste the following contents to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-webkit-linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#e2e2e2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#d7dceb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#e2e2e2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, visit &lt;a href="http://localhost:3333/styles.css" rel="noopener noreferrer"&gt;http://localhost:3333/styles.css&lt;/a&gt; to access the file inside the browser.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605592988%2Fdevto%2Fserve-css-file.png" 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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605592988%2Fdevto%2Fserve-css-file.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Lets open &lt;code&gt;resources/views/todos/index.edge&lt;/code&gt; and load this css file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Other tags --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same process can be repeated for JavaScript files, images, fonts, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using assets bundler
&lt;/h2&gt;

&lt;p&gt;In the previous section, we accomplished the task of serving static files by adding them to the &lt;code&gt;public&lt;/code&gt; folder. Of course, these files have to be written in a way that browsers can understand, parse, and execute.&lt;/p&gt;

&lt;p&gt;However, we live in a complicated world. We are so much used to using pre/post processors. Writing code that browsers cannot fully understand. Therefore we need tools like Webpack to compile our version of code into something different browsers can understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Laravel Mix
&lt;/h3&gt;

&lt;p&gt;The Laravel community (Jeffrey Way to be specific) created &lt;a href="https://laravel-mix.com" rel="noopener noreferrer"&gt;Laravel mix&lt;/a&gt; which wraps webpack into a high level, less verbose API. Even though the package uses the keyword &lt;code&gt;Laravel&lt;/code&gt;, it can be used independently as well.&lt;/p&gt;

&lt;p&gt;Luckily, there is also &lt;a href="https://github.com/wahyubucil/adonis-mix-asset" rel="noopener noreferrer"&gt;a package&lt;/a&gt; for AdonisJS that eases the setup process of using Laravel mix in AdonisJS apps.&lt;/p&gt;

&lt;p&gt;So let's start by installing it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; adonis-mix-asset laravel-mix@next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command to configure the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace invoke adonis-mix-asset

&lt;span class="c"&gt;# CREATE: webpack.mix.js&lt;/span&gt;
&lt;span class="c"&gt;# UPDATE: .adonisrc.json { commands += "adonis-mix-asset/build/commands" }&lt;/span&gt;
&lt;span class="c"&gt;# UPDATE: .adonisrc.json { providers += "adonis-mix-asset" }&lt;/span&gt;
&lt;span class="c"&gt;# CREATE: ace-manifest.json file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And start the compilation process by running the following ace command. Also, feel free to reference the &lt;a href="https://github.com/wahyubucil/adonis-mix-asset/blob/master/README.md" rel="noopener noreferrer"&gt;README file&lt;/a&gt; of the adonis-mix-asset package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace mix:watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup TailwindCSS
&lt;/h3&gt;

&lt;p&gt;We are all set now! Before giving this set up a test run let's also install and configure &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss

&lt;span class="c"&gt;# Create a Tailwind config file&lt;/span&gt;
npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;webpack.mix.js&lt;/code&gt; file and add the following line of code inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postCss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resources/css/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here we tell mix to process the &lt;code&gt;resources/css/styles.css&lt;/code&gt; file using PostCSS.&lt;/li&gt;
&lt;li&gt;The output should be written to the &lt;code&gt;public/css&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Also, we are using tailwind as a plugin of PostCSS.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Why resources folder and not public?&lt;/strong&gt;&lt;br&gt;
If you noticed we are telling PostCSS to read the file from the &lt;code&gt;resources/css&lt;/code&gt; folder and not the public folder.&lt;/p&gt;

&lt;p&gt;The PostCSS syntax is not something the browsers can understand and hence there is no point in keeping this file inside the &lt;code&gt;public&lt;/code&gt; folder. Instead, we want the processed output to be in the &lt;code&gt;public&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;This is true for every other file including the frontend JavaScript, images, and so on. Any asset that needs pre-processing should not be in the &lt;code&gt;public&lt;/code&gt; folder.&lt;/p&gt;



&lt;p&gt;Let's remove everything we added to the &lt;code&gt;public&lt;/code&gt; folder earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; public/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file named &lt;code&gt;css/styles.css&lt;/code&gt; inside the &lt;code&gt;resources&lt;/code&gt; directory and paste the following contents inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we need to update our template to load the CSS file created by Laravel mix inside the &lt;code&gt;public&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/css/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's give it a try now. Run the following commands to start the HTTP server and the mix process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Starts AdonisJS server&lt;/span&gt;
node ace serve &lt;span class="nt"&gt;--watch&lt;/span&gt;

&lt;span class="c"&gt;# Inside another terminal session&lt;/span&gt;
node ace mix:watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the webpage inside the browser and for sure you will see TailwindCSS getting loaded on the page.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605596414%2Fdevto%2Floading-tailwind_rs3pxe.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605596414%2Fdevto%2Floading-tailwind_rs3pxe.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Alpine.Js
&lt;/h3&gt;

&lt;p&gt;Let's quickly follow the same process for setting up Alpine.js. Begin by installing the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;alpinejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a JavaScript file named &lt;code&gt;app.js&lt;/code&gt; inside the &lt;code&gt;resources/js&lt;/code&gt; directory and paste the following contents inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alpinejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, tell mix to pre-process this file by adding the following line of code to the &lt;code&gt;webpack.mix.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;js&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resources/js/app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, make sure to restart the &lt;code&gt;node ace mix:watch&lt;/code&gt; command for the mix to pick up the config file changes.&lt;/p&gt;

&lt;p&gt;Finally, we can load the processed JavaScript file inside the head tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To give it a test run, modify the &lt;code&gt;body&lt;/code&gt; tag as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt; &lt;span class="na"&gt;x-init=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; alert('Alpine is ready')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Todos app&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;We will being rendering todos here&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing notes
&lt;/h2&gt;

&lt;p&gt;Initially, I decided to even design the webpage in the post. However, we already covered a lot of ground, so let's move the design phase to the next post.&lt;/p&gt;

&lt;p&gt;Today we learned about&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rendering views using the &lt;code&gt;ctx.view.render&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Server front-end assets by keeping them inside the &lt;code&gt;public&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Setup &lt;code&gt;adonis-mix-asset&lt;/code&gt; to process the front-end assets and write them to the &lt;code&gt;public&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>adonisjs</category>
      <category>tailwindcss</category>
      <category>node</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Routes &amp; Controllers</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Fri, 13 Nov 2020 08:32:08 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-routes-controllers-51l6</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-routes-controllers-51l6</guid>
      <description>&lt;p&gt;Let's start dipping our toes in the code now by creating some routes and controllers.&lt;/p&gt;

&lt;p&gt;For anyone unaware of the term routing. In terms of web development, it is a mapping of URLs and their handlers that you want your app to handle. URLs outside of this mapping will result in a 404.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining routes
&lt;/h2&gt;

&lt;p&gt;Routes in AdonisJS are defined inside the &lt;code&gt;start/routes.ts&lt;/code&gt; file. Using this file is a convention and not a technical limitation. Let's open the file and replace its contents with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is the home page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is the about page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/projects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is the page to list projects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We begin by importing the &lt;code&gt;Route&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;Route.get&lt;/code&gt; method, we define a total of 3 routes.&lt;/li&gt;
&lt;li&gt;A typical route accepts a &lt;strong&gt;route pattern&lt;/strong&gt; and a &lt;strong&gt;handler&lt;/strong&gt; to respond to the requests.&lt;/li&gt;
&lt;li&gt;In the above example, the handler is an inline function. Later we will look into using controllers as well.&lt;/li&gt;
&lt;li&gt;Finally, the return value of the function is sent back to the client making the request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's give this code a try by visiting the URLs for the registered routes.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605247138%2Fdevto%2Froutes-demo_gm4wck.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605247138%2Fdevto%2Froutes-demo_gm4wck.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Supported data types
&lt;/h3&gt;

&lt;p&gt;You can return most of the Javascript data types from the route handler and AdonisJS will properly serialize them for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// return 28&lt;/span&gt;
  &lt;span class="c1"&gt;// return new Date()&lt;/span&gt;
  &lt;span class="c1"&gt;// return { hello: 'world' }&lt;/span&gt;
  &lt;span class="c1"&gt;// return [1, 2, 3]&lt;/span&gt;
  &lt;span class="c1"&gt;// return false&lt;/span&gt;
  &lt;span class="c1"&gt;// return '&amp;lt;h1&amp;gt; Hello world &amp;lt;/h1&amp;gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HTTP Context
&lt;/h3&gt;

&lt;p&gt;Every route handler receives an instance of the HTTP context as the first parameter. The context holds all the information related to the current request, along with the &lt;code&gt;response&lt;/code&gt; object to customize the HTTP response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following is the output of the &lt;code&gt;ctx.inspect()&lt;/code&gt;.&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605247676%2Fdevto%2Fctx-inspect.png" 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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605247676%2Fdevto%2Fctx-inspect.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are coming from a framework like express, then there is no &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt; objects in AdonisJS. Instead you have access to &lt;code&gt;ctx.request&lt;/code&gt; and &lt;code&gt;ctx.response&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also note that the API of &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; is not compatible with express and neither it is a goal for us.&lt;/p&gt;

&lt;p&gt;The HTTP context has an extendible API and many AdonisJS packages add their properties to the context. For example: If you install the &lt;a href="https://preview.adonisjs.com/guides/auth/introduction" rel="noopener noreferrer"&gt;@adonisjs/auth&lt;/a&gt; module, it will add the &lt;code&gt;ctx.auth&lt;/code&gt; property.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using controllers
&lt;/h3&gt;

&lt;p&gt;Controllers in AdonisJS are vanilla ES6 classes stored inside the &lt;code&gt;app/Controllers/Http&lt;/code&gt; directory. You can create a new controller by running the following ace command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:controller TodoController

&lt;span class="c"&gt;# CREATE: app/Controllers/Http/TodosController.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's open the newly created file and replace its contents with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/HttpContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodosController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world from the todos controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;How should we now go about using this controller inside our routes file?&lt;/strong&gt;&lt;br&gt;
Let's begin with zero magic and simply import the controller inside the routes file. Open the &lt;code&gt;start/routes.ts&lt;/code&gt; file and add another route using the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TodosController&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Controllers/Http/TodosController&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TodosController&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit the &lt;a href="http://localhost:3333/todos" rel="noopener noreferrer"&gt;http://localhost:3333/todos&lt;/a&gt; URL and you will surely see the return value from the controller method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy loading controllers
&lt;/h3&gt;

&lt;p&gt;Now, imagine an app with 40-50 controllers. Every controller will also have its own set of imports, making the routes file a choke point.&lt;/p&gt;

&lt;p&gt;Lazy loading is the perfect solution to the above problem. Instead of importing all the controllers at the top level, we can lazily import them within the route's handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TodosController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Controllers/Http/TodosController&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TodosController&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the &lt;code&gt;TodosController&lt;/code&gt; is only loaded when the request for the &lt;code&gt;/todos&lt;/code&gt; route comes in. Since the import/require statements are cached in Node.js, you don't have to worry about reading the same file multiple times from the disk.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Are are you happy with the above code?&lt;/strong&gt;&lt;br&gt;
I am personally not. There is too much boilerplate and you would expect a framework to do a better job here and cleanup things for you and AdonisJS does that.&lt;/p&gt;

&lt;p&gt;Replace the previously written route with the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TodosController.index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the recommended way of referencing controllers within your routes file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We already know that your controllers are inside the &lt;code&gt;app/Controllers/Http&lt;/code&gt; directory and hence there is no need to define the complete path.&lt;/li&gt;
&lt;li&gt;You only need to define the file name and the method to be called on the exported class.&lt;/li&gt;
&lt;li&gt;Behind the scenes, AdonisJS will lazily import the controller. Creates an instance of it and executes the referenced method.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What about the type safety?&lt;/strong&gt;&lt;br&gt;
The verbose implementation has the extra benefit of being type safe. This is something missing when using the string based expression. Or I will say, it is missing for now.&lt;/p&gt;

&lt;p&gt;We need two things to achieve type safety when referencing &lt;code&gt;controller.method&lt;/code&gt; as a string expression.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The ability to tokenize the expression and create a full path to the controller and its method. This is achievable with &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#template-literal-types" rel="noopener noreferrer"&gt;Typescript 4.1 beta release&lt;/a&gt;. Here is a &lt;a href="https://www.typescriptlang.org/play?ts=4.1.0-beta#code/MYewdgzgLgBASiArlApjAvDA3gKBjAcxSgB4AJAQzABMAbFAJxhQA9UaIZoGBLMAgHwAKAA4UoqBmABcXKL34AaGAAsqdRrMo16DAJSyY2jU1btqnAAYASLHwBmjGAEEAvgDpbDpwCFXlmAB+bDx8GFAweRBaXVlLZxERAHoAYXAomMYIJLIJZNs3S0VQ-ABbYhUQalkfUNcYWUQwAGswEAB3MBxXHF6kpKMQADcnYacoFTQAIgYkVCmYIYpeCgAjehh1LhQ0CfEYdrRlo-XdkBgxBggjuQUCGD4oc6fmlEgcCOgYWeQ0TARfu4iFAhAByJKg5SgsggcppSKzTIMdx8aisUF6IA" rel="noopener noreferrer"&gt;proof of concept&lt;/a&gt; for the same.&lt;/li&gt;
&lt;li&gt;Next is the ability to have an &lt;code&gt;Import&lt;/code&gt; type with support for generics. There is an &lt;a href="https://github.com/microsoft/TypeScript/issues/31090" rel="noopener noreferrer"&gt;open issue for it&lt;/a&gt; and I am positive that it will make its way to the Typescript in the future, as it adheres to the Typescript design goals.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To summarize, we bet in the future of Typescript and decided to remove all the extra boilerplate required to reference controllers within the routes file and expose a simple to use API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Alright, let's wrap up this post. In the next post, we will begin designing the web page for our todo app.&lt;/p&gt;

&lt;p&gt;Meanwhile, lemme share some code examples for commonly required tasks that you may perform when creating a web app.&lt;/p&gt;
&lt;h3&gt;
  
  
  Render views
&lt;/h3&gt;

&lt;p&gt;Render views using the AdonisJS &lt;a href="https://preview.adonisjs.com/guides/views/introduction" rel="noopener noreferrer"&gt;template engine&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Writing an article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modify outgoing response
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/to/a/url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-powered-by&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-app-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stream files from the disk
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/some/file.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Read request data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

  &lt;span class="c1"&gt;// request body + query string&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="c1"&gt;// get a single file &amp;amp; validate it too&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2mb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;extnames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// All uploaded files as an object&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>adonisjs</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Part 3</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Thu, 12 Nov 2020 07:39:04 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-part-3-65k</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-part-3-65k</guid>
      <description>&lt;p&gt;We got up and running with a new application in the previous post. In this post, lets will take a step ahead and learn about some of the key concepts of AdonisJS.&lt;/p&gt;

&lt;p&gt;I think it is very important to know about your tools before you starting using them. So let's spend another post without writing any code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ace commands
&lt;/h2&gt;

&lt;p&gt;In this last post, we start the development server by running the &lt;code&gt;node ace serve --watch&lt;/code&gt; command, so let's start by breaking it down&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;node&lt;/code&gt; is the node binary installed on your computer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ace&lt;/code&gt; is a Javascript file in the root of your project. We removed the &lt;code&gt;.js&lt;/code&gt; extension, coz it is easier to type &lt;code&gt;node ace&lt;/code&gt; over &lt;code&gt;node ace.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;After the &lt;code&gt;ace&lt;/code&gt; keyword is the command name and the arguments/flags required by the command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why the hell did not create a command-line binary that can be installed globally?&lt;/strong&gt;&lt;br&gt;
You might be thinking, why there isn't a command-line binary that can be installed globally and then use it as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adonis serve &lt;span class="nt"&gt;--watch&lt;/span&gt;
adonis make:controller &amp;lt;Name&amp;gt;

&lt;span class="c"&gt;# and so on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer is, &lt;strong&gt;you cannot have project-specific commands with a global binary&lt;/strong&gt; and a backend server usually needs project-specific commands. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A command to run queue workers&lt;/li&gt;
&lt;li&gt;A command to migrate the database&lt;/li&gt;
&lt;li&gt;Or maybe if you play fancy, then command to set up the project by creating an admin user and seeding data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AdonisJS is not the first one to do this. &lt;strong&gt;Django has &lt;code&gt;manage.py&lt;/code&gt; file&lt;/strong&gt;. &lt;strong&gt;Laravel has an &lt;code&gt;artisan&lt;/code&gt; file&lt;/strong&gt;. However, when looking at the Node.js landscape, the concept feels a bit alien, hence requires explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Directory structure
&lt;/h2&gt;

&lt;p&gt;The default project structure of AdonisJS contains a bunch of files and folders by default. You may get overwhelmed at first by looking at the directory structure, especially when you are new to the framework. So, let's go ahead and talk about the important files and folders.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;.adonisrc.json&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;.adonisrc.json&lt;/code&gt; is one of the most important files as it is responsible for configuring your project and workspace.&lt;/p&gt;

&lt;p&gt;Even though the file contains the bare minimum config by default. It allows overriding almost every convention used by the framework.&lt;/p&gt;

&lt;p&gt;Run the following command to see the complete set of available options and their pre-configured defaults.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace dump:rcfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605156716%2Fdevto%2Frc-dump_unkfh1.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fv1605156716%2Fdevto%2Frc-dump_unkfh1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  About Fx
  &lt;a href="https://github.com/antonmedv/fx" rel="noopener noreferrer"&gt;fx&lt;/a&gt; is a commandline utility to make the JSON output interactive 

&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;server.ts&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;server.ts&lt;/code&gt; file is the entry point of your HTTP server. Running this file directly will boot your application and then starts the HTTP server.&lt;/p&gt;

&lt;p&gt;This is a file you will run on your production server after it gets compiled to Javascript.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;app&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; directory contains most of your application code. HTTP controllers, middleware, models, services, and much more live inside this folder.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;config&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;config&lt;/code&gt; directory is dedicated to storing all the configuration required by your app. By default, we create a handful of well-documented configuration files that are used by the framework core and first-party packages.&lt;/p&gt;

&lt;p&gt;As your application will grow, you can also use this directory to store additional configuration files.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;start&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; directory contains all the files that you want to load only once during the application boot phase. We also call them preloaded files.&lt;/p&gt;

&lt;p&gt;There is no technical limitation or hard coded rule that files inside this directory will be loaded only once. But more of a convention we follow to communicate a clear intent.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;resources&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;resources&lt;/code&gt; directory is dedicated to storing edge templates inside a subdirectory called &lt;code&gt;views&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is also recommended to use this directory also for storing un-compiled frontend assets like &lt;strong&gt;SASS files&lt;/strong&gt; or &lt;strong&gt;frontend JavaScript&lt;/strong&gt;. After compiling the frontend assets, they must be moved to the &lt;code&gt;public&lt;/code&gt; directory, since the &lt;code&gt;resources&lt;/code&gt; directory is not exposed to the internet.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;public&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;public&lt;/code&gt; directory is exposed to the internet and all the files can be accessed by their path.&lt;/p&gt;

&lt;p&gt;Given the following files inside the &lt;code&gt;public&lt;/code&gt; folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;public
├── app.js
├── logo.png
└── style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access them as&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:3333/app.js" rel="noopener noreferrer"&gt;http://localhost:3333/app.js&lt;/a&gt;&lt;br&gt;
&lt;a href="http://localhost:3333/logo.png" rel="noopener noreferrer"&gt;http://localhost:3333/logo.png&lt;/a&gt;&lt;br&gt;
&lt;a href="http://localhost:3333/style.css" rel="noopener noreferrer"&gt;http://localhost:3333/style.css&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;database&lt;/code&gt; directory
&lt;/h4&gt;

&lt;p&gt;AdonisJS recommends storing the database &lt;strong&gt;schema migrations&lt;/strong&gt;, &lt;strong&gt;seeders&lt;/strong&gt; and &lt;strong&gt;factories&lt;/strong&gt; inside the database directory.&lt;/p&gt;

&lt;p&gt;We keep them outside of the &lt;code&gt;app&lt;/code&gt; directory because they are usually not part of the runtime code and get executed as a separate step during development or pre-deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typescript first
&lt;/h2&gt;

&lt;p&gt;AdonisJS is a Typescript first framework. Beyond using it as a marketing term, we have invested a lot in building first-class primitives for a better typescript experience. In a nutshell, it includes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First-class support for &lt;a href="https://preview.adonisjs.com/blog/october-2020-release#in-memory-typescript-compilation" rel="noopener noreferrer"&gt;running Typescript source directly&lt;/a&gt; without compiling during development.&lt;/li&gt;
&lt;li&gt;Inbuilt command &lt;code&gt;node ace build&lt;/code&gt; to create a standalone &lt;code&gt;build&lt;/code&gt; folder that can be deployed to the production server.&lt;/li&gt;
&lt;li&gt;Support for &lt;a href="https://preview.adonisjs.com/blog/october-2020-release#validating-environment-variables" rel="noopener noreferrer"&gt;static types for environment variables&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Data validator that &lt;a href="https://preview.adonisjs.com/blog/introducing-adonisjs-v5#full-typed-request-body" rel="noopener noreferrer"&gt;extracts static types&lt;/a&gt; after performing the runtime validations.&lt;/li&gt;
&lt;li&gt;Application-aware &lt;a href="https://preview.adonisjs.com/guides/repl" rel="noopener noreferrer"&gt;REPL&lt;/a&gt; that can compile and run the typescript code directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find value in adopting Typescript, then you can also bet on AdonisJS, as we are always looking for ways to fully embrace typescript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fully featured, not full-stack
&lt;/h2&gt;

&lt;p&gt;AdonisJS is a fully featured and not a full-stack framework. The term full-stack is generally used when someone or something is equipped with both the web frontend and the backend.&lt;/p&gt;

&lt;p&gt;AdonisJS has zero opinions on how you create your frontend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Want to server render HTML?&lt;/strong&gt;
We got your back with a pretty decent template engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want to use React, Vue, or Svelte?&lt;/strong&gt;
Use AdonisJS to create a JSON API and then develop your frontend as an independent app consuming the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want to develop a Mobile app?&lt;/strong&gt;
Again, consume the same API and build a native mobile app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of us think that if a framework is not full-stack, then it is a micro-framework. For god sake, there are a gazillion things that we do and should do on the backend. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managing the data layer wisely. It includes sanitizing data, hashing/encrypting values with secret keys, and a much more&lt;/li&gt;
&lt;li&gt;User authentication and authorization&lt;/li&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Queue jobs&lt;/li&gt;
&lt;li&gt;Validations&lt;/li&gt;
&lt;li&gt;Managing file uploads&lt;/li&gt;
&lt;li&gt;Interacting with 3rd party APIs. No, you cannot connect with 3rd party APIs securely from your frontend app.&lt;/li&gt;
&lt;li&gt;Payments &amp;amp; Subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fully-featured framework like AdonisJS makes all this easy.&lt;/p&gt;

</description>
      <category>node</category>
      <category>adonisjs</category>
    </item>
    <item>
      <title>Introducing AdonisJS - Setup</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Wed, 11 Nov 2020 07:05:20 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-part-2-2aa8</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-part-2-2aa8</guid>
      <description>&lt;p&gt;In this post, we will focus on setting up the development environment and also get our code editor ready to work with Typescript and Edge (the template engine of AdonisJS)&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;AdonisJS is a Node.js framework and therefore you must have Node.js installed on your computer.&lt;/p&gt;

&lt;p&gt;If it's not installed, then please head over to &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt; and download the binary/installer for your operating system. If you are comfortable with the command line, then I recommend using &lt;a href="https://volta.sh" rel="noopener noreferrer"&gt;volta&lt;/a&gt; &lt;strong&gt;(personal favorite)&lt;/strong&gt; or &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;nvm&lt;/a&gt; for installing Node.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Node.js version
&lt;/h3&gt;

&lt;p&gt;Make sure that the installed version is greater than &lt;code&gt;12.0.0&lt;/code&gt; along with &lt;code&gt;npm &amp;gt;= 6.0.0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
npm &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you need :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new project
&lt;/h2&gt;

&lt;p&gt;Creating a new AdonisJS project is pretty straight forward. All you need to do is run the following &lt;code&gt;npm init&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init adonis-ts-app todo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fq_100%2Fv1605073459%2Fdevto%2Fadonis-new_yve3iz.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fq_100%2Fv1605073459%2Fdevto%2Fadonis-new_yve3iz.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here &lt;code&gt;npm init&lt;/code&gt; is an npm specific command to install a package and run its bin command in one go.&lt;/li&gt;
&lt;li&gt;The package name is &lt;a href="https://www.npmjs.com/package/create-adonis-ts-app" rel="noopener noreferrer"&gt;create-adonis-ts-app&lt;/a&gt;, but &lt;code&gt;npm init&lt;/code&gt; wants us to drop the &lt;code&gt;create&lt;/code&gt; prefix. Not sure about the history behind this decision, but the yarn has the same design as well.&lt;/li&gt;
&lt;li&gt;Finally &lt;code&gt;todo&lt;/code&gt; is the path in the filesystem where you want to create the project. If the directory is missing, we will create it for you. However, if the directory already has files then we will refuse to create a project inside it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Differences between the &lt;code&gt;web&lt;/code&gt; and the &lt;code&gt;api&lt;/code&gt; project structure
&lt;/h3&gt;

&lt;p&gt;The project creation flow prompts you to choose between the &lt;code&gt;api&lt;/code&gt; and the &lt;code&gt;web&lt;/code&gt; project structure. The following are the differences between both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;web&lt;/code&gt; project structure installs the &lt;a href="https://preview.adonisjs.com/guides/views/introduction" rel="noopener noreferrer"&gt;template engine&lt;/a&gt; and the &lt;a href="https://preview.adonisjs.com/guides/http/sessions" rel="noopener noreferrer"&gt;session package&lt;/a&gt; of AdonisJS. Also, the support for serving static files like &lt;strong&gt;CSS&lt;/strong&gt; or &lt;strong&gt;images&lt;/strong&gt; is enabled.&lt;/li&gt;
&lt;li&gt;On the other hand, the &lt;code&gt;api&lt;/code&gt; project structure configures the support for CORS.&lt;/li&gt;
&lt;li&gt;Apart from these specific changes, the rest of the project structure and files are the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting the development server
&lt;/h2&gt;

&lt;p&gt;Run the following command to start the development server. Make sure that you are inside the project root before running the command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace serve &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fq_100%2Fv1605074788%2Fdevto%2Fadonis-serve_a6h0k5.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%2Fres.cloudinary.com%2Fadonis-js%2Fimage%2Fupload%2Fq_100%2Fv1605074788%2Fdevto%2Fadonis-serve_a6h0k5.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here things get a bit interesting. We will talk about &lt;strong&gt;ace commands&lt;/strong&gt; in the next post. But for now, &lt;code&gt;ace&lt;/code&gt; is a command-line framework that is embedded within your app and this allows you to create project-specific commands and run them using &lt;code&gt;node ace&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;serve&lt;/code&gt; command starts the development server and the &lt;code&gt;--watch&lt;/code&gt; flag continues watching the filesystem for changes and restarts the server after every change.&lt;/li&gt;
&lt;li&gt;Also, the &lt;code&gt;serve&lt;/code&gt; command can run the typescript source directly. There is no intermediate compilation step and this speeds up things a little bit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  VSCode setup
&lt;/h2&gt;

&lt;p&gt;To begin with, I am going to share a list of extensions that you must install for VSCode. If you are not a VSCode user, then feel free to leave comments and I will update the blog post with other editors setup too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source code is written in Typescript and luckily VSCode comes with out of the box support it and hence no special plugins are required to work with Typescript.&lt;/li&gt;
&lt;li&gt;You must install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=luongnd.edge" rel="noopener noreferrer"&gt;Edge template support&lt;/a&gt; plugin to have syntax highlighting for the AdonisJS template engine (Edge).&lt;/li&gt;
&lt;li&gt;If you don't hold strong opinions against prettier and eslint. Then I highly recommend &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;VScode Eslint&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;VSCode prettier&lt;/a&gt; plugins to make your life a bit easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's in the box?
&lt;/h2&gt;

&lt;p&gt;Since AdonisJS is a fully-featured framework, you get a lot with a new application. The following features or functionality is baked right into the core of the framework.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP stack. It includes the router, request &amp;amp; response objects, support for middleware, global exception handler.&lt;/li&gt;
&lt;li&gt;Bodyparser and support for file uploads. Even stuff like directly streaming files to a third party like s3.&lt;/li&gt;
&lt;li&gt;Validator to validate the request data.&lt;/li&gt;
&lt;li&gt;Encryption and Hash modules with sensible defaults by keeping overall security in mind.&lt;/li&gt;
&lt;li&gt;An embedded command line framework &lt;code&gt;ace&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Support for encrypted/signed cookies and session.&lt;/li&gt;
&lt;li&gt;Support for serving static files from the &lt;code&gt;public&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;And finally, a well thought project structure and out of the box support for typescript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This post is mainly targeted towards the audience using AdonisJS or Node.js for the first time. I want to talk about every little detail without making assumptions that the reader already knows this.&lt;/p&gt;

&lt;p&gt;If you think all this is a no-brainer for you, then stick around, things will get interesting as we will make progress 🙂&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
    </item>
    <item>
      <title>Introducing AdonisJS</title>
      <dc:creator>Aman Virk</dc:creator>
      <pubDate>Tue, 10 Nov 2020 04:35:37 +0000</pubDate>
      <link>https://dev.to/amanvirk1/introducing-adonisjs-28af</link>
      <guid>https://dev.to/amanvirk1/introducing-adonisjs-28af</guid>
      <description>&lt;p&gt;Hello everyone 👋. I am Aman Virk, the creator of &lt;a href="https://preview.adonisjs.com"&gt;AdonisJS&lt;/a&gt; &lt;strong&gt;(a fully-featured web framework for Node.js)&lt;/strong&gt;. Today I am starting a blog posts series to introduce you to the framework, where we together build a little todo app.&lt;/p&gt;

&lt;p&gt;If you are someone who is just starting with Node.js or looking to explore a framework that you can use going forward, then I welcome you to follow the entire series and also become part of the AdonisJS community 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;Even though the app we are building doesn't have many features, this series will still get quite long, as I will be covering a lot of fundamentals initially.&lt;/p&gt;

&lt;p&gt;The outline briefly looks as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction ( this post )&lt;/li&gt;
&lt;li&gt;Setup&lt;/li&gt;
&lt;li&gt;Get to know AdonisJS&lt;/li&gt;
&lt;li&gt;Routing &amp;amp; Controllers&lt;/li&gt;
&lt;li&gt;Creating &amp;amp; designing pages&lt;/li&gt;
&lt;li&gt;Building frontend assets&lt;/li&gt;
&lt;li&gt;Planning the todo app&lt;/li&gt;
&lt;li&gt;ORM setup&lt;/li&gt;
&lt;li&gt;Creating models &amp;amp; migrations&lt;/li&gt;
&lt;li&gt;Using the AdonisJS REPL&lt;/li&gt;
&lt;li&gt;Rendering todos&lt;/li&gt;
&lt;li&gt;Creating a todo&lt;/li&gt;
&lt;li&gt;Marking a Todo as done&lt;/li&gt;
&lt;li&gt;Deleting a todo&lt;/li&gt;
&lt;li&gt;Deploying to Digital ocean via Cleavr&lt;/li&gt;
&lt;li&gt;Deploying to Digital ocean via App platform&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why AdonisJS?
&lt;/h2&gt;

&lt;p&gt;This might be the first question you have in your mind. &lt;strong&gt;"Why should I even give AdonisJS a try?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Luckily, answering this question is easy enough for me, since AdonisJS proposes a different paradigm for writing your web servers in Node.js. It's possible that you may or may not like the paradigm altogether, but the framework itself is unique enough to not cause any confusion for its existence.&lt;/p&gt;

&lt;p&gt;The popular approach I have always witnessed in Node.js is to start with microframeworks like Express, Koa, or Fastify and then build things on top of it. Even the basic stuff like parsing the request body, validating forms, the logger has to be pulled in separately and &lt;strong&gt;AdonisJS is the opposite of this&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Philosophically AdonisJS follows the approach of Laravel, Ruby on Rails, or Django. We believe that baseline features for creating a web server are pretty much settled and asking someone to configure them manually has no upsides. Features like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing the request body&lt;/li&gt;
&lt;li&gt;Validating form data&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;li&gt;Logging throughout the framework&lt;/li&gt;
&lt;li&gt;Cookies + session management&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;SQL ORM&lt;/li&gt;
&lt;li&gt;Template engine&lt;/li&gt;
&lt;li&gt;Support for sending emails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and much more are already baked into AdonisJS so that you can kick start your projects vs spending time assembling your projects.&lt;/p&gt;

&lt;p&gt;Along with the benefits of rapidly developing your apps. There are a few more advantages of using a fully-featured framework like AdonisJS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API across all the packages of the framework is consistent and follows similar naming and code conventions. In turn, there is a less cognitive load on you.&lt;/li&gt;
&lt;li&gt;Since the framework already knows a lot about your application, it can ship exclusive tooling to further improve your development experience. &lt;a href="https://preview.adonisjs.com/guides/repl"&gt;AdonisJS REPL&lt;/a&gt; is a great example of the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The goal of this post was to mainly introduce you to the framework. Moving forward, we will be writing a lot of code using &lt;strong&gt;AdonisJS&lt;/strong&gt;, &lt;strong&gt;Tailwind CSS&lt;/strong&gt;, and a little bit of &lt;strong&gt;AlpineJS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I will be writing one blog post every day and all you need is to take out 30-45mins and follow along as we create our little todo app. Meanwhile, you can visit the following links to learn more about AdonisJS and show some love ❤️&lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/adonisframework"&gt;https://twitter.com/adonisframework&lt;/a&gt;&lt;br&gt;
Github:  &lt;a href="https://github.com/adonisjs/core"&gt;https://github.com/adonisjs/core&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://preview.adonisjs.com"&gt;https://preview.adonisjs.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>node</category>
      <category>adonisjs</category>
    </item>
  </channel>
</rss>
