<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on Mihai Saru</title>
        <link>/posts/</link>
        <description>Recent content in Posts on Mihai Saru</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Tue, 22 Dec 2020 18:06:04 +0200</lastBuildDate>
        <atom:link href="/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>Svelte starter with Typescript, Tailwindcss, routing &amp; lazy loaded routes - IE11 compatible</title>
            <link>/posts/svelte-typescript-tailwindcss-ie11-starter/</link>
            <pubDate>Tue, 22 Dec 2020 18:06:04 +0200</pubDate>
            
            <guid>/posts/svelte-typescript-tailwindcss-ie11-starter/</guid>
            <description>1. Prerequisites:  node (used here v14.15.0)  2. Project initialisation with Typescript Init the project (named here svelte-project-starter) using degit:
npx degit sveltejs/template svelte-project-starter Navigate inside the directory of the newly created project:
cd ./svelte-project-starter Convert the project to Typescript:
node scripts/setupTypeScript.js Install the node modules:
npm i (Optional) Add Prettier config: It&amp;rsquo;s useful to first install prettier-plugin-svelte:
npm i -D prettier-plugin-svelte The .prettierrc file:
{ &amp;#34;printWidth&amp;#34;: 120, &amp;#34;singleQuote&amp;#34;: true, &amp;#34;useTabs&amp;#34;: false, &amp;#34;tabWidth&amp;#34;: 2, &amp;#34;semi&amp;#34;: false, &amp;#34;bracketSpacing&amp;#34;: true, &amp;#34;trailingComma&amp;#34;: &amp;#34;es5&amp;#34;, &amp;#34;svelteSortOrder&amp;#34;: &amp;#34;scripts-markup-styles&amp;#34; } The .</description>
            <content type="html"><![CDATA[<h2 id="1-prerequisites">1. Prerequisites:</h2>
<ul>
<li>node (used here <strong>v14.15.0</strong>)</li>
</ul>
<h2 id="2-project-initialisation-with-typescript">2. Project initialisation with Typescript</h2>
<p>Init the project (named here <strong>svelte-project-starter</strong>) using <code>degit</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npx degit sveltejs/template svelte-project-starter
</code></pre></div><p>Navigate inside the directory of the newly created project:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">cd ./svelte-project-starter
</code></pre></div><p>Convert the project to <strong>Typescript</strong>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">node scripts/setupTypeScript.js
</code></pre></div><p>Install the node modules:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i
</code></pre></div><h3 id="optional-add-prettier-config">(Optional) Add Prettier config:</h3>
<p>It&rsquo;s useful to first install <code>prettier-plugin-svelte</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -D prettier-plugin-svelte
</code></pre></div><p>The <em>.prettierrc</em> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-JSON" data-lang="JSON">{
  <span style="color:#f92672">&#34;printWidth&#34;</span>: <span style="color:#ae81ff">120</span>,
  <span style="color:#f92672">&#34;singleQuote&#34;</span>: <span style="color:#66d9ef">true</span>,
  <span style="color:#f92672">&#34;useTabs&#34;</span>: <span style="color:#66d9ef">false</span>,
  <span style="color:#f92672">&#34;tabWidth&#34;</span>: <span style="color:#ae81ff">2</span>,
  <span style="color:#f92672">&#34;semi&#34;</span>: <span style="color:#66d9ef">false</span>,
  <span style="color:#f92672">&#34;bracketSpacing&#34;</span>: <span style="color:#66d9ef">true</span>,
  <span style="color:#f92672">&#34;trailingComma&#34;</span>: <span style="color:#e6db74">&#34;es5&#34;</span>,

  <span style="color:#f92672">&#34;svelteSortOrder&#34;</span>: <span style="color:#e6db74">&#34;scripts-markup-styles&#34;</span>
}
</code></pre></div><p>The <em>.prettierignore</em> file:</p>
<pre><code>rollup.config.js
tailwind.config.js
package.json
package-lock.json
tslint.json
tsconfig.json
public/build
.github/
</code></pre><p>You should now be able to succesfuly run the app in development mode at <code>localhost:5000</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm run dev
</code></pre></div><h2 id="3-add-tailwindcss">3. Add tailwindcss</h2>
<p>In order to stay compatible with <strong>IE 11</strong>, we will use <code>tailwindcss@1.9.x</code>.
Tailwind css depends on <code>postcss@8</code>, and we will install <code>autoprefixer</code> &amp; <code>postcss-nesting</code>
to make development more pleasurable.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -D tailwindcss@1.9.x autoprefixer postcss-nesting postcss@8
</code></pre></div><h4 id="tailwindcss-configuration">Tailwindcss configuration</h4>
<p>The <em>tailwind.config.js</em> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">production</span> <span style="color:#f92672">=</span> <span style="color:#f92672">!</span><span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">ROLLUP_WATCH</span>;

<span style="color:#a6e22e">module</span>.<span style="color:#a6e22e">exports</span> <span style="color:#f92672">=</span> {
  <span style="color:#a6e22e">future</span><span style="color:#f92672">:</span> {
    <span style="color:#a6e22e">purgeLayersByDefault</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    <span style="color:#a6e22e">removeDeprecatedGapUtilities</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
  },
  <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [],
  <span style="color:#a6e22e">purge</span><span style="color:#f92672">:</span> {
    <span style="color:#a6e22e">content</span><span style="color:#f92672">:</span> [
      <span style="color:#e6db74">&#39;./src/**/*.svelte&#39;</span>,
      <span style="color:#75715e">// may also want to include base index.html
</span><span style="color:#75715e"></span>    ],
    <span style="color:#a6e22e">enabled</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">production</span> <span style="color:#75715e">// disable purge in dev
</span><span style="color:#75715e"></span>  }
}
</code></pre></div><h4 id="update-the-rollup-configuration">Update the Rollup configuration</h4>
<p>Add the <strong>postcss</strong> config in the <strong>sveltePreprocess</strong> plugin:</p>
<p>The <em>rollup.config.js</em> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// ...
</span><span style="color:#75715e"></span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> {
  <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [
    <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">svelte</span>({
      <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>      <span style="color:#a6e22e">preprocess</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">sveltePreprocess</span>({
        <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>        <span style="color:#a6e22e">sourceMap</span><span style="color:#f92672">:</span> <span style="color:#f92672">!</span><span style="color:#a6e22e">production</span>,
        <span style="color:#a6e22e">postcss</span><span style="color:#f92672">:</span> {
          <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [
            <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;tailwindcss&#39;</span>),
            <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;autoprefixer&#39;</span>),
            <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;postcss-nesting&#39;</span>),
          ],
        },
      })
    })
  ]
}
</code></pre></div><h3 id="import-tailwindcss-in-the-components">Import Tailwindcss in the components</h3>
<p>Next, we need to import <strong>tailwindcss</strong> somewhere in our app code.</p>
<p>This is best done in the root component of the app, in order to
have access to the CSS classes in every component the app uses.</p>
<p>In the <code>./src/App.svelte</code> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span>&gt;
&lt;/<span style="color:#f92672">script</span>&gt;

&lt;<span style="color:#f92672">style</span> <span style="color:#a6e22e">global</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;postcss&#34;</span>&gt;
  <span style="color:#75715e">/* purgecss start ignore */</span>
  @<span style="color:#66d9ef">tailwind</span> <span style="color:#f92672">base</span>;
  @<span style="color:#66d9ef">tailwind</span> <span style="color:#f92672">components</span>;
  <span style="color:#75715e">/* purgecss end ignore */</span>

  @<span style="color:#66d9ef">tailwind</span> <span style="color:#f92672">utilities</span>;
&lt;/<span style="color:#f92672">style</span>&gt;

&lt;<span style="color:#f92672">main</span>&gt;
  &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-xl text-red-200&#34;</span>&gt;Svelte, Typescript, Tailwindcss&lt;/<span style="color:#f92672">h1</span>&gt;
&lt;/<span style="color:#f92672">main</span>&gt;
</code></pre></div><h3 id="test-everything-works">Test everything works</h3>
<p>Now, if you run <code>npm run dev</code> one more time, the compilation should
be successful, and the heading should appear as a red text, with the size
of <code>1.25rem</code>.</p>
<h3 id="test-that-the-css-purging-works-in-production-mode">Test that the CSS purging works in production mode</h3>
<p>In <strong>development</strong> mode, the size of the css bundle exceeds <strong>1M</strong>.</p>
<p>However, if everything in the setup is correct, the size in production should
be much smaller because every unused CSS class and asset from <strong>tailwindcss</strong>
will be purged from the final build.</p>
<p>For example, at the time of this writing, after I created a production build
with <code>npm run build</code>, the size of the files found in <code>./public/build</code> were:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>bundle.css</td>
<td>3.6K</td>
</tr>
<tr>
<td>bundle.js</td>
<td>2.4K</td>
</tr>
</tbody>
</table>
<p><strong>Note:</strong> The production build can be served with <code>npm start</code>.</p>
<h2 id="4-add-ie-11-compatibility">4. Add IE 11 compatibility</h2>
<h3 id="add-babel-to-transpile-the-code-to-es5">Add babel to transpile the code to ES5</h3>
<p>Install the required packages:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -D @babel/core @babel/preset-env @babel/plugin-transform-runtime <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>         @babel/plugin-syntax-dynamic-import @rollup/plugin-babel
</code></pre></div><p>We also need to install <code>core-js</code> for ES6 features in ES5:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -D core-js
</code></pre></div><h3 id="update-the-rollup-configuration-to-use-babel">Update the Rollup configuration to use babel</h3>
<p>The <code>rollup.config.js</code> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">import</span> <span style="color:#a6e22e">babel</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@rollup/plugin-babel&#39;</span>;

<span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> {
  <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [
    <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>
    <span style="color:#75715e">// for IE 11 compatibility
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">babel</span>({
      <span style="color:#a6e22e">extensions</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;.js&#39;</span>, <span style="color:#e6db74">&#39;.mjs&#39;</span>, <span style="color:#e6db74">&#39;.html&#39;</span>, <span style="color:#e6db74">&#39;.svelte&#39;</span>],
      <span style="color:#a6e22e">babelHelpers</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;runtime&#39;</span>,
      <span style="color:#a6e22e">exclude</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;node_modules/@babel/**&#39;</span>, <span style="color:#e6db74">&#39;node_modules/core-js/**&#39;</span>],
      <span style="color:#a6e22e">presets</span><span style="color:#f92672">:</span> [
        [
          <span style="color:#e6db74">&#39;@babel/preset-env&#39;</span>,
          {
            <span style="color:#a6e22e">targets</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&gt; 0.25%, not dead&#39;</span>,
            <span style="color:#a6e22e">useBuiltIns</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;usage&#39;</span>,
            <span style="color:#a6e22e">corejs</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">3</span>,
          }
        ]
      ],
      <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [
        <span style="color:#e6db74">&#39;@babel/plugin-syntax-dynamic-import&#39;</span>,
        [
          <span style="color:#e6db74">&#39;@babel/plugin-transform-runtime&#39;</span>,
          {
            <span style="color:#a6e22e">useESModules</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>
          }
        ]
      ]
    }),
  ]
}
</code></pre></div><h3 id="test-everything-works-1">Test everything works</h3>
<p>If everything went as intended, if you run <code>npm run dev</code> again, you now should
be able to see the app running without errors in a IE11 browser.</p>
<h3 id="how-the-bundle-size-was-affected">How the bundle size was affected</h3>
<p>After running <code>npm run build</code> for a production build, the sizes of the
generated files are as follows:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>bundle.css</td>
<td>3.6K</td>
</tr>
<tr>
<td>bundle.js</td>
<td>46K</td>
</tr>
</tbody>
</table>
<p>Although the <code>bundle.js</code> size increased dramatically, it is still way better than
the other popular frameworks like <strong>Angular</strong>, <strong>React</strong> or <strong>Vue</strong>. I guess this
is the price we have to pay for having to support <strong>IE11</strong>.</p>
<h2 id="5-add-routing">5. Add routing</h2>
<h3 id="install">Install</h3>
<p>For the routing we are going to use the declarative <strong><a href="https://www.npmjs.com/package/svelte-routing">svelte-routing</a></strong> library:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -D svelte-routing
</code></pre></div><h3 id="create-some-page-components">Create some page components</h3>
<p>Let&rsquo;s create some demo pages (<strong>Svelte</strong> components):</p>
<p>The <code>./src/page/Home.svelte</code> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-xl&#34;</span>&gt;Home Page&lt;/<span style="color:#f92672">h1</span>&gt;
</code></pre></div><p>The <code>./src/page/About.svelte</code> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-xl&#34;</span>&gt;About Page&lt;/<span style="color:#f92672">h1</span>&gt;
</code></pre></div><h3 id="define-the-router-viewport-and-routes">Define the router viewport and routes</h3>
<p>The <code>./src/App.svelte</code> file will look like this:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span>&gt;
<span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Router</span>, <span style="color:#a6e22e">Link</span>, <span style="color:#a6e22e">Route</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;svelte-routing&#39;</span>
<span style="color:#66d9ef">import</span> <span style="color:#a6e22e">Home</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;./page/Home.svelte&#39;</span>
<span style="color:#66d9ef">import</span> <span style="color:#a6e22e">About</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;./page/About.svelte&#39;</span>

<span style="color:#66d9ef">export</span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">url</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>
&lt;/<span style="color:#f92672">script</span>&gt;

&lt;<span style="color:#f92672">main</span>&gt;
  &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-xl text-red-500&#34;</span>&gt;Svelte, Typescript, Tailwindcss&lt;/<span style="color:#f92672">h1</span>&gt;
  &lt;<span style="color:#f92672">Router</span> <span style="color:#a6e22e">ulr</span><span style="color:#f92672">=</span><span style="color:#e6db74">{url}</span>&gt;
    <span style="color:#75715e">&lt;!-- Navigation bar with links to the different pages --&gt;</span>
    &lt;<span style="color:#f92672">nav</span>&gt;
      &lt;<span style="color:#f92672">Link</span> <span style="color:#a6e22e">to</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;home&#34;</span>&gt;Home&lt;/<span style="color:#f92672">Link</span>&gt;
      &lt;<span style="color:#f92672">Link</span> <span style="color:#a6e22e">to</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;about&#34;</span>&gt;About&lt;/<span style="color:#f92672">Link</span>&gt;
    &lt;/<span style="color:#f92672">nav</span>&gt;

    <span style="color:#75715e">&lt;!-- Viewport where the routes are linked to the component pages --&gt;</span>
    &lt;<span style="color:#f92672">div</span>&gt;
      &lt;<span style="color:#f92672">Route</span> <span style="color:#a6e22e">path</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;home&#34;</span> <span style="color:#a6e22e">component</span><span style="color:#f92672">=</span><span style="color:#e6db74">{Home}</span> /&gt;
      &lt;<span style="color:#f92672">Route</span> <span style="color:#a6e22e">path</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;about&#34;</span> <span style="color:#a6e22e">component</span><span style="color:#f92672">=</span><span style="color:#e6db74">{About}</span> /&gt;
    &lt;/<span style="color:#f92672">div</span>&gt;
  &lt;/<span style="color:#f92672">Router</span>&gt;
&lt;/<span style="color:#f92672">main</span>&gt;

<span style="color:#75715e">&lt;!-- The style content remains unchanged --&gt;</span>
&lt;<span style="color:#f92672">style</span> <span style="color:#a6e22e">global</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;postcss&#34;</span>&gt;
  <span style="color:#f92672">//</span> <span style="color:#f92672">...</span>
&lt;/<span style="color:#f92672">style</span>&gt;
</code></pre></div><h3 id="update-the-npm-scripts-to-redirect-to-indexhtml-on-404">Update the npm scripts to redirect to index.html on 404</h3>
<p>To allow direct navigation by putting the url in the browser (for example
trying to navigate directly to <code>http://localhost:5000/home</code>), we need to
tell svelte server (<strong>sirv</strong>) to redirect to <code>index.html</code> when the requested
page is not found.</p>
<p>We do this by updating the <code>start</code> script in <code>package.json</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-JSON" data-lang="JSON">{
  <span style="color:#960050;background-color:#1e0010">//</span> <span style="color:#960050;background-color:#1e0010">...</span>
  <span style="color:#f92672">&#34;scripts&#34;</span>: {
    <span style="color:#960050;background-color:#1e0010">//</span> <span style="color:#960050;background-color:#1e0010">...</span>
    <span style="color:#f92672">&#34;start&#34;</span>: <span style="color:#e6db74">&#34;sirv public --single&#34;</span>
  }
}
</code></pre></div><p><strong>Important note</strong>: This will only work for serving the project after building it
with <code>npm run build</code>.</p>
<p>I don&rsquo;t have a solution for <code>npm run dev</code> yet, but I will update this article once
I&rsquo;ll find it.</p>
<h3 id="test-everything-works-2">Test everything works</h3>
<p>If everything went as intended, if you run <code>npm run build</code> and <code>npm start</code> you should
be able to see the <strong>Home</strong> and <strong>About</strong> links and to navigate to them.</p>
<h3 id="how-the-bundle-size-was-affected-1">How the bundle size was affected</h3>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>bundle.css</td>
<td>3.6K</td>
</tr>
<tr>
<td>bundle.js</td>
<td>64K</td>
</tr>
</tbody>
</table>
<h2 id="6-add-lazy-loading-bundle-chunking-with-es-native-modules">6. Add lazy loading (bundle chunking with ES native modules)</h2>
<h3 id="create-a-component-that-we-will-lazy-load-from-one-of-the-previous-defined-routes">Create a component that we will lazy load from one of the previous defined routes:</h3>
<p>The component <code>./src/component/LazyLoaded.svelte</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">section</span>&gt;
  &lt;<span style="color:#f92672">h1</span>&gt;Lazy loaded component!&lt;/<span style="color:#f92672">h1</span>&gt;
&lt;/<span style="color:#f92672">section</span>&gt;
</code></pre></div><h3 id="update-the-about-page-to-lazy-load-the-component">Update the About page to lazy load the component</h3>
<p>Lazy load the component using ES dynamic imports.</p>
<p>The updated <code>./src/page/About.svelte</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML">&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span>&gt;
  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">LazyLoadedP</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">import</span>(<span style="color:#e6db74">&#39;../component/LazyLoaded.svelte&#39;</span>)
    .<span style="color:#a6e22e">then</span>(({ <span style="color:#66d9ef">default</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">C</span> }) =&gt; <span style="color:#a6e22e">C</span>)
&lt;/<span style="color:#f92672">script</span>&gt;

&lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-xl&#34;</span>&gt;About Page&lt;/<span style="color:#f92672">h1</span>&gt;
{#await LazyLoadedP}
  ...Loading lazy loaded component
{:then LazyLoaded}
  &lt;<span style="color:#f92672">LazyLoaded</span> /&gt;
{/await}
</code></pre></div><h3 id="update-rollup-configuration-and-indexhtml">Update rollup configuration and index.html</h3>
<p>Update the <code>rollup.config.js</code> file as follows:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> {
  <span style="color:#75715e">// ...
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">output</span><span style="color:#f92672">:</span> [
    <span style="color:#75715e">// The order counts! Otherwise, we get an error at build time!
</span><span style="color:#75715e"></span>    {
      <span style="color:#a6e22e">sourcemap</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
      <span style="color:#a6e22e">format</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;esm&#39;</span>,
      <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;app&#39;</span>,
      <span style="color:#a6e22e">dir</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;public/build&#39;</span>
    },
    {
      <span style="color:#a6e22e">sourcemap</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
      <span style="color:#a6e22e">format</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;iife&#39;</span>,
      <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;app&#39;</span>,
      <span style="color:#a6e22e">file</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;public/build/main.iife.js&#39;</span>,

      <span style="color:#75715e">// this is important, otherwise we get an error:
</span><span style="color:#75715e"></span>      <span style="color:#75715e">// can&#39;t use IIFE format with dynamic imports!
</span><span style="color:#75715e"></span>      <span style="color:#a6e22e">inlineDynamicImports</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    },
  ],
}
</code></pre></div><p>Update the <code>./public/index.html</code> file as follows:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-HTML" data-lang="HTML"><span style="color:#75715e">&lt;!DOCTYPE html&gt;</span>
<span style="color:#75715e">&lt;!-- ... --&gt;</span>
&lt;<span style="color:#f92672">head</span>&gt;
  <span style="color:#75715e">&lt;!-- ... --&gt;</span>

  <span style="color:#75715e">&lt;!-- We indicate that the file type is module (it contains ES import statements) --&gt;</span>
  &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">defer</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;module&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/build/main.js&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;

  <span style="color:#75715e">&lt;!-- the only browsers that will load this file are --&gt;</span>
  <span style="color:#75715e">&lt;!-- the ones which don&#39;t support ES modules --&gt;</span>
  <span style="color:#75715e">&lt;!-- nomodule indicates to modern browser that this file should not be loaded --&gt;</span>
  &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">nomodule</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;build/main.iife.js&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
&lt;/<span style="color:#f92672">head</span>&gt;
</code></pre></div><h3 id="test-everything-works-3">Test everything works</h3>
<p>To test everything works as intended, run <code>npm run build</code> and then <code>npm start</code>. When you
navigate to the app in a browser that supports <strong>ES modules</strong>, in the network tab you
should see that already the browser loaded two JS files: <code>main.js</code> and another chunk <code>main-[hash].js</code></p>
<p>Now, keeping the network tab open, go to the <strong>About</strong> page. In the network tab, another
request for the <code>LazyLoaded-[hash].js</code> should appear. This is the JS file for our
<code>LazyLoaded.svelte</code> component.</p>
<p>Now, if you open the app in <strong>IE 11</strong> everything should work as it previously did.
The only difference will be that the file loaded by <strong>IE 11</strong> will be called
<code>main.iife.js</code> instead of <code>bundle.js</code>. <strong>IE 11</strong> will also load the <code>main.js</code> file,
but this should not interfere.</p>
<h3 id="how-the-bundle-size-was-affected-2">How the bundle size was affected</h3>
<h4 id="files-loaded-at-startup-modern-browsers">Files loaded at startup (modern browsers):</h4>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>bundle.css</td>
<td>3.6K</td>
</tr>
<tr>
<td>main.js</td>
<td>79B</td>
</tr>
<tr>
<td>main.[hash].js</td>
<td>66K</td>
</tr>
</tbody>
</table>
<h4 id="module-files">Module files:</h4>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>LazyLoaded.[hash].js</td>
<td>0.9K</td>
</tr>
</tbody>
</table>
<h4 id="ie-11-files">IE 11 files:</h4>
<table>
<thead>
<tr>
<th>File</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>bundle.css</td>
<td>3.6K</td>
</tr>
<tr>
<td>main.iife.js</td>
<td>67K</td>
</tr>
</tbody>
</table>
<h2 id="7-conclusion">7. Conclusion</h2>
<p>If you followed the steps, now you should have a completly working project starter
in Svelte, with <strong>routing</strong>, <strong>TailwindCSS</strong> setup and <strong>lazy loaded modules</strong>, that also works on <strong>IE 11</strong>.</p>
]]></content>
        </item>
        
        <item>
            <title>API Data Model in Adonis and Postgres</title>
            <link>/posts/adonis-postgres-data-model/</link>
            <pubDate>Sat, 14 Mar 2020 13:27:49 +0200</pubDate>
            
            <guid>/posts/adonis-postgres-data-model/</guid>
            <description>Prerequisites A fresh Adonis project configured with a running Postgres DB. You can create one by following this guide.
Fresh start Make sure that there is no migration run until this point by executing:
adonis migration:status If there are migrations that have already been ran, reset them by executing:
adonis migration:reset Data model we are going to implement The model consists of three entities: user, post, and post_status.
The details of the entitites and the relationships between them are presented below:</description>
            <content type="html"><![CDATA[<h3 id="prerequisites">Prerequisites</h3>
<p>A fresh Adonis project configured with a running Postgres DB. You can create one by following
<a href="/posts/adonis-4_1-postgres-starter/">this guide</a>.</p>
<h4 id="fresh-start">Fresh start</h4>
<p>Make sure that there is no migration run until this point by executing:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis migration:status
</code></pre></div><p>If there are migrations that have already been ran, reset them by executing:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis migration:reset
</code></pre></div><h3 id="data-model-we-are-going-to-implement">Data model we are going to implement</h3>
<p>The model consists of three entities: <strong>user</strong>, <strong>post</strong>, and <strong>post_status</strong>.</p>
<p>The details of the entitites and the relationships between them are presented below:</p>
<p><img src="/img/sql-model.png" alt="Data model"></p>
<h3 id="setup-table-schemas-and-lucid-orm-models">Setup table schemas and Lucid ORM models</h3>
<h4 id="update-the-schema-and-model-for-the-entities-created-by-adonis-users-and-tokens">Update the schema and model for the entities created by Adonis (users and tokens)</h4>
<p>Delete the <strong>username</strong> and <strong>password</strong> columns from the <strong>user</strong> migration schema (we will not need them):</p>
<p>(Also, rename the table from <strong>users</strong> to <strong>user</strong>).</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/migrations/*_user.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UserSchema</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Schema</span> {
  <span style="color:#a6e22e">up</span>() {
    <span style="color:#75715e">// updated from users to user
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">create</span>(<span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#a6e22e">table</span> =&gt; {
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">increments</span>()
      <span style="color:#a6e22e">table</span>
        .<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;email&#39;</span>, <span style="color:#ae81ff">254</span>)
        .<span style="color:#a6e22e">notNullable</span>()
        .<span style="color:#a6e22e">unique</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">timestamps</span>()
    })
  }

  <span style="color:#a6e22e">down</span>() {
    <span style="color:#75715e">// updated from users to user
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">drop</span>(<span style="color:#e6db74">&#39;user&#39;</span>)
  }
}
</code></pre></div><p>Update the tokens table name to <strong>token</strong> (singular form):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/migrations/*_token.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TokensSchema</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Schema</span> {
  <span style="color:#a6e22e">up</span>() {
    <span style="color:#75715e">// updated from tokens to token
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">create</span>(<span style="color:#e6db74">&#39;token&#39;</span>, <span style="color:#a6e22e">table</span> =&gt; {
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">increments</span>()
      <span style="color:#a6e22e">table</span>
        .<span style="color:#a6e22e">integer</span>(<span style="color:#e6db74">&#39;user_id&#39;</span>)
        .<span style="color:#a6e22e">unsigned</span>()
        .<span style="color:#a6e22e">references</span>(<span style="color:#e6db74">&#39;id&#39;</span>)
        <span style="color:#75715e">// updated from users to user
</span><span style="color:#75715e"></span>        .<span style="color:#a6e22e">inTable</span>(<span style="color:#e6db74">&#39;user&#39;</span>)
      <span style="color:#a6e22e">table</span>
        .<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;token&#39;</span>, <span style="color:#ae81ff">255</span>)
        .<span style="color:#a6e22e">notNullable</span>()
        .<span style="color:#a6e22e">unique</span>()
        .<span style="color:#a6e22e">index</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;type&#39;</span>, <span style="color:#ae81ff">80</span>).<span style="color:#a6e22e">notNullable</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#66d9ef">boolean</span>(<span style="color:#e6db74">&#39;is_revoked&#39;</span>).<span style="color:#a6e22e">defaultTo</span>(<span style="color:#66d9ef">false</span>)
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">timestamps</span>()
    })
  }

  <span style="color:#a6e22e">down</span>() {
    <span style="color:#75715e">// updated from tokens to token
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">drop</span>(<span style="color:#e6db74">&#39;token&#39;</span>)
  }
}
</code></pre></div><p>When working with models, Adonis assumes by default that the table names are in plural form.
Throughout this guide we will update every model to work with the singular form of the
coresponding table name by adding the <code>get table()</code> static method to the model class.
This is just my preference.</p>
<p>Update the <strong>User</strong> model:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/User.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;user&#39;</span>
  }

  <span style="color:#75715e">// other code
</span><span style="color:#75715e"></span>}
</code></pre></div><p>Update the <strong>Token</strong> model:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/Token.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Token</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;token&#39;</span>
  }
}

</code></pre></div><h4 id="create-the-poststatus-schema-and-model">Create the PostStatus schema and model</h4>
<p>The migrations need to start from the entities that don&rsquo;t depend on other entities.</p>
<p>Create the <strong>PostStatus</strong> model (pass <code>-m</code> to also create the schema migration file):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis make:model -m PostStatus
</code></pre></div><p>Update the table name to <strong>post_status</strong> (singular form) in
the model:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/PostStatus.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">PostStatus</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;post_status&#39;</span>
  }
}
</code></pre></div><p>Update the table name to <strong>post_status</strong> (singular form) and add our column definitions
in the migration schema:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/migrations/*_post_status_schema.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">PostStatusSchema</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Schema</span> {
  <span style="color:#a6e22e">up</span>() {
    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">create</span>(<span style="color:#e6db74">&#39;post_status&#39;</span>, <span style="color:#a6e22e">table</span> =&gt; {
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">increments</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;name&#39;</span>, <span style="color:#ae81ff">255</span>)
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;description&#39;</span>, <span style="color:#ae81ff">1000</span>)
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;code&#39;</span>, <span style="color:#ae81ff">50</span>).<span style="color:#a6e22e">unique</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">timestamps</span>()
    })
  }

  <span style="color:#a6e22e">down</span>() {
    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">drop</span>(<span style="color:#e6db74">&#39;post_status&#39;</span>)
  }
}
</code></pre></div><h4 id="create-the-post-schema-and-model">Create the Post schema and model</h4>
<p>Create the <strong>Post</strong> model (pass <code>-m</code> to also create the schema migration file):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis make:model -m Post
</code></pre></div><p>Update the model to use the new table name <strong>post</strong> (singular form) in the model:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/Post.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Post</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;post&#39;</span>
  }
}
</code></pre></div><p>Update the table name to <strong>post</strong> (singular form) in the migration schema and add
our column definitions:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/migrations/*_post_schema.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">PostSchema</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Schema</span> {
  <span style="color:#a6e22e">up</span>() {
    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">create</span>(<span style="color:#e6db74">&#39;post&#39;</span>, <span style="color:#a6e22e">table</span> =&gt; {
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">increments</span>()
      <span style="color:#a6e22e">table</span>
        .<span style="color:#a6e22e">integer</span>(<span style="color:#e6db74">&#39;user_id&#39;</span>)
        .<span style="color:#a6e22e">references</span>(<span style="color:#e6db74">&#39;id&#39;</span>)
        .<span style="color:#a6e22e">inTable</span>(<span style="color:#e6db74">&#39;user&#39;</span>)
      <span style="color:#a6e22e">table</span>
        .<span style="color:#a6e22e">integer</span>(<span style="color:#e6db74">&#39;post_status_id&#39;</span>)
        .<span style="color:#a6e22e">references</span>(<span style="color:#e6db74">&#39;id&#39;</span>)
        .<span style="color:#a6e22e">inTable</span>(<span style="color:#e6db74">&#39;post_status&#39;</span>)
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;title&#39;</span>, <span style="color:#ae81ff">255</span>).<span style="color:#a6e22e">notNullable</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">string</span>(<span style="color:#e6db74">&#39;content&#39;</span>, <span style="color:#ae81ff">1000</span>).<span style="color:#a6e22e">notNullable</span>()
      <span style="color:#a6e22e">table</span>.<span style="color:#a6e22e">timestamps</span>()
    })
  }

  <span style="color:#a6e22e">down</span>() {
    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">drop</span>(<span style="color:#e6db74">&#39;post&#39;</span>)
  }
}
</code></pre></div><h4 id="define-the-relationships-between-entities">Define the relationships between entities</h4>
<p>Add the <strong>user</strong> relationship to the <strong>posts</strong>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/User.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#75715e">// other code
</span><span style="color:#75715e"></span>
  <span style="color:#a6e22e">posts</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">hasMany</span>(<span style="color:#e6db74">&#39;App/Models/Post&#39;</span>)
  }
}
</code></pre></div><p>Add the <strong>post status</strong> relationship to the <strong>post</strong>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/PostStatus.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">PostStatus</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;post_status&#39;</span>
  }

  <span style="color:#a6e22e">posts</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">hasMany</span>(<span style="color:#e6db74">&#39;App/Models/Post&#39;</span>)
  }
}
</code></pre></div><h4 id="create-the-tables">Create the tables</h4>
<p>Run the migrations using:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis migration:run
</code></pre></div><p>It should output <code>Database migrated successfully</code> and if you refresh the <em>Adminer</em> page you should
see the new tables.</p>
<h3 id="seed-data">Seed data</h3>
<p>It&rsquo;s nice to have some data created automatically so we have something to work with.</p>
<p>Run the following command to create the <strong>Dev</strong> seeder. This will seed data for the <em>development environment</em>.
Later you might want to define a <strong>Prod</strong> seeder, which will seed data for the <em>production environment</em>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis make:seed Dev
</code></pre></div><h4 id="create-the-factories">Create the factories</h4>
<p>Factories are used in the seeder to easily create entities with mocked (and some real) data.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/factory.js
</span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">Factory</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">use</span>(<span style="color:#e6db74">&#39;Factory&#39;</span>)

<span style="color:#a6e22e">Factory</span>.<span style="color:#a6e22e">blueprint</span>(<span style="color:#e6db74">&#39;App/Models/User&#39;</span>, <span style="color:#a6e22e">faker</span> =&gt; {
  <span style="color:#66d9ef">return</span> {
    <span style="color:#a6e22e">email</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">email</span>({ <span style="color:#a6e22e">domain</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;example.com&#39;</span> }),
  }
})

<span style="color:#a6e22e">Factory</span>.<span style="color:#a6e22e">blueprint</span>(<span style="color:#e6db74">&#39;App/Models/Post&#39;</span>, <span style="color:#a6e22e">faker</span> =&gt; {
  <span style="color:#66d9ef">return</span> {
    <span style="color:#a6e22e">title</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">sentence</span>(),
    <span style="color:#a6e22e">content</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">faker</span>.<span style="color:#a6e22e">paragraph</span>(),
  }
})
</code></pre></div><p>Here, the <strong>faker</strong> object is an instance of the <a href="https://chancejs.com/" title="chance.js library documentation">chance.js library</a>.</p>
<h4 id="create-the-seeder">Create the seeder</h4>
<p>Seeders are the guys who put the seed data (which can come from the factories defined above) into the DB.</p>
<p>There will be only two <strong>post_statuses</strong>, so we define them separately before the seeder class:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/seeds/DevSeeder.js
</span><span style="color:#75715e"></span>
<span style="color:#75715e">// other code
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">POST_STATUS_DRAFT</span> <span style="color:#f92672">=</span> {
  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Draft&#39;</span>,
  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;This post is a work in progress.&#39;</span>,
  <span style="color:#a6e22e">code</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;DRAFT&#39;</span>,
}
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">POST_STATUS_PUBLISHED</span> <span style="color:#f92672">=</span> {
  <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Published&#39;</span>,
  <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;This post is published.&#39;</span>,
  <span style="color:#a6e22e">code</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;PUBLISHED&#39;</span>,
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DevSeeder</span> {
  <span style="color:#75715e">// other code
</span><span style="color:#75715e"></span>}
</code></pre></div><p>In the code below we define the seeder logic. First, note that we also get the PostStatus model
from the DI container. Next, the logic is as follows:</p>
<ol>
<li>
<p>Create the entities without dependencies first.</p>
</li>
<li>
<p>For every non dependent entity, create the entities that depend on them and save them to the
database through the parents relationship defined in the <strong>Model</strong>. In this way, the Lucid ORM
will also automatically create the connection between the entities in the DB (by setting the
foreign keys).</p>
</li>
</ol>
<p>One important thing to note here is that I haven&rsquo;t yet found a way to seed data for entities that
have multiple foreign keys that are <code>notNullable()</code>. The reason is that you can&rsquo;t set an ORM
relationship without trying to save the parent entity to the DB, meaning you can only set 1 foreign key
bofore the <code>INSERT</code> operation is performed. Of course, the DB will throw an error because we are trying
to set <code>NULL</code> values to <code>notNullable()</code> fields.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: database/seeds/DevSeeder.js
</span><span style="color:#75715e"></span>
<span style="color:#75715e">//other code
</span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">Factory</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">use</span>(<span style="color:#e6db74">&#39;Factory&#39;</span>)

<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">PostStatus</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">use</span>(<span style="color:#e6db74">&#39;App/Models/PostStatus&#39;</span>)

<span style="color:#75715e">// other code
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DevSeeder</span> {
  <span style="color:#66d9ef">async</span> <span style="color:#a6e22e">run</span>() {
    <span style="color:#75715e">// Create the user model instances (without inserting them into DB).
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">users</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">Factory</span>.<span style="color:#a6e22e">model</span>(<span style="color:#e6db74">&#39;App/Models/User&#39;</span>).<span style="color:#a6e22e">makeMany</span>(<span style="color:#ae81ff">5</span>)

    <span style="color:#75715e">// Create and insert the postStatus model instances into the DB.
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">postStatusDraft</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">PostStatus</span>.<span style="color:#a6e22e">create</span>(<span style="color:#a6e22e">POST_STATUS_DRAFT</span>)
    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">postStatusPublished</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">PostStatus</span>.<span style="color:#a6e22e">create</span>(<span style="color:#a6e22e">POST_STATUS_PUBLISHED</span>)

    <span style="color:#75715e">// For each user create 2 posts to be saved as DRAFT and 3 posts
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// to be saved as published.
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// Then associate the user and the postStatus to the posts by
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// inserting the posts to the DB through the relationships
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// with the aforementioned entities.
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">users</span>.<span style="color:#a6e22e">forEach</span>(<span style="color:#66d9ef">async</span> <span style="color:#a6e22e">user</span> =&gt; {
      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">postsDraft</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">Factory</span>.<span style="color:#a6e22e">model</span>(<span style="color:#e6db74">&#39;App/Models/Post&#39;</span>).<span style="color:#a6e22e">makeMany</span>(<span style="color:#ae81ff">2</span>)
      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">postsPublished</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">Factory</span>.<span style="color:#a6e22e">model</span>(<span style="color:#e6db74">&#39;App/Models/Post&#39;</span>).<span style="color:#a6e22e">makeMany</span>(<span style="color:#ae81ff">3</span>)

      <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">postStatusDraft</span>.<span style="color:#a6e22e">posts</span>().<span style="color:#a6e22e">saveMany</span>(<span style="color:#a6e22e">postsDraft</span>)
      <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">postStatusPublished</span>.<span style="color:#a6e22e">posts</span>().<span style="color:#a6e22e">saveMany</span>(<span style="color:#a6e22e">postsPublished</span>)

      <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">posts</span>().<span style="color:#a6e22e">saveMany</span>(<span style="color:#a6e22e">postsDraft</span>)
      <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">posts</span>().<span style="color:#a6e22e">saveMany</span>(<span style="color:#a6e22e">postsPublished</span>)
    })
  }
}
</code></pre></div><h4 id="run-the-seed-and-populate-the-db">Run the seed and populate the DB</h4>
<p>Run the seed with:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis seed
</code></pre></div><p>It should output <code>Seeded database</code> and if you refresh <em>Adminer</em> you should now see the
<strong>user</strong>, <strong>post_status</strong>, and <strong>post</strong> tables populated with data.</p>
<h3 id="ending">Ending</h3>
<p>This is it! I hope this article provides a good starting point in modelling and seeding your
data in <em>Adonis</em> and <em>PostgreSQL</em>.</p>
<p>In the future this article may get updated if I come by any other difficulties which are worthy
to be exemplified here.</p>
<h3 id="important-notes">Important notes</h3>
<h4 id="sometimes-the-sql-relationships-are-not-intuitive">Sometimes the SQL relationships are not intuitive</h4>
<p>Altough at first glance it appears that the relationship between a <strong>post</strong> and a <strong>post_status</strong> is a
<em>hasOne</em> relationship, that&rsquo;s not true - it would mean that every post has a separate row in the <strong>post_status</strong>
column. Actually, in the SQL sense, one <strong>post_status</strong> <em>hasMany</em> <strong>posts</strong>.</p>
<p>This is how I naively defined the relationship at first:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#75715e">// file: app/Models/Post.js
</span><span style="color:#75715e"></span>
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Post</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Model</span> {
  <span style="color:#66d9ef">static</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">table</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;post&#39;</span>
  }

  <span style="color:#a6e22e">postStatus</span>() {
    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">hasOne</span>(<span style="color:#e6db74">&#39;App/Models/PostStatus&#39;</span>)
  }
}
</code></pre></div><h3 id="common-errors">Common errors</h3>
<p>If you get <code>TypeError: Cannot read property 'name' of undefined</code> when you try to run the seed using <em>factories</em>,
you may have misspelled the name of the model in the factory declaration (or there is no factory defined for
that model).</p>
<p>If you get <code>TypeError: relatedInstance.save is not a function</code> you may have called the <code>save</code> method instead of the
<code>saveMany</code> for saving an array of model instances.</p>
]]></content>
        </item>
        
        <item>
            <title>AdonisJS 4.1 API with PosgreSQL starter</title>
            <link>/posts/adonis-4_1-postgres-starter/</link>
            <pubDate>Fri, 13 Mar 2020 17:00:00 +0200</pubDate>
            
            <guid>/posts/adonis-4_1-postgres-starter/</guid>
            <description>Prerequisites:  node (used here v13.9.0) docker (used here v19.03.5) docker-compose (used here v1.25.4)  Install @adonisjs/cli globally:
Create a new --api-only project using the CLI:
adonis new adonis-postgres-demo-project --api-only Run npm i pg so that our project can connect to PostgreSQL.
Add the .prettierrc file:
{ &amp;#34;printWidth&amp;#34;: 120, &amp;#34;singleQuote&amp;#34;: true, &amp;#34;useTabs&amp;#34;: false, &amp;#34;tabWidth&amp;#34;: 2, &amp;#34;semi&amp;#34;: false, &amp;#34;bracketSpacing&amp;#34;: true, &amp;#34;trailingComma&amp;#34;: &amp;#34;es5&amp;#34; } Create the Dockerfile:
FROMnode:13.10.1-alpine3.10# so that we can use the adonis command to serve the app in --dev modeRUN npm i -g @adonisjs/cli@4.</description>
            <content type="html"><![CDATA[<h3 id="prerequisites">Prerequisites:</h3>
<ul>
<li>node (used here v13.9.0)</li>
<li>docker (used here v19.03.5)</li>
<li>docker-compose (used here v1.25.4)</li>
</ul>
<p>Install <code>@adonisjs/cli</code> globally:</p>
<p>Create a new <code>--api-only</code> project using the CLI:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis new adonis-postgres-demo-project --api-only
</code></pre></div><p>Run <code>npm i pg</code> so that our project can connect to PostgreSQL.</p>
<p>Add the <em>.prettierrc</em> file:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-JSON" data-lang="JSON">{
  <span style="color:#f92672">&#34;printWidth&#34;</span>: <span style="color:#ae81ff">120</span>,
  <span style="color:#f92672">&#34;singleQuote&#34;</span>: <span style="color:#66d9ef">true</span>,
  <span style="color:#f92672">&#34;useTabs&#34;</span>: <span style="color:#66d9ef">false</span>,
  <span style="color:#f92672">&#34;tabWidth&#34;</span>: <span style="color:#ae81ff">2</span>,
  <span style="color:#f92672">&#34;semi&#34;</span>: <span style="color:#66d9ef">false</span>,
  <span style="color:#f92672">&#34;bracketSpacing&#34;</span>: <span style="color:#66d9ef">true</span>,
  <span style="color:#f92672">&#34;trailingComma&#34;</span>: <span style="color:#e6db74">&#34;es5&#34;</span>
}
</code></pre></div><p>Create the <em>Dockerfile</em>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Docker" data-lang="Docker"><span style="color:#66d9ef">FROM</span><span style="color:#e6db74"> node:13.10.1-alpine3.10</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># so that we can use the adonis command to serve the app in --dev mode</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> npm i -g @adonisjs/cli@4.0.12<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">WORKDIR</span><span style="color:#e6db74"> /usr/src/app</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">COPY</span> package.json ./package.json<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> npm install<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># copy all project files to the container</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">COPY</span> . .<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># start the adonis server in --dev mode</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">CMD</span> adonis serve --dev<span style="color:#960050;background-color:#1e0010">
</span></code></pre></div><p>And the <em>.dockerignorefile</em>:</p>
<pre><code>node_modules
</code></pre><p>Update the .env file to use <code>HOST=0.0.0.0</code> instead of <code>HOST=127.0.0.1</code> to make the API available outside of the localhost of the docker container.</p>
<p>Run <code>docker build -t adonis-demo-project .</code> to build the Docker image named <em>adonis-demo-project:latest</em>.</p>
<p>Run the image with:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker run --rm -it -p 3333:3333 adonis-demo-project:latest
</code></pre></div><p>The API should now be accessible in the browser at the address <code>http://localhost:3333</code> and should respond with the following JSON:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-JSON" data-lang="JSON">{<span style="color:#f92672">&#34;greeting&#34;</span>:<span style="color:#e6db74">&#34;Hello world in JSON&#34;</span>}
</code></pre></div><p>You can now stop the container with <em>Ctrl-C</em>.</p>
<p>Create the <em>docker-compose.yaml</em> file (which also setups volumes so that the code updates done in the project are reflected immediately in the docker container):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-YAML" data-lang="YAML"><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#39;3.7&#39;</span>

<span style="color:#f92672">services</span>:
  <span style="color:#f92672">api</span>:
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">adonis-demo-api</span>
    <span style="color:#f92672">build</span>:
      <span style="color:#f92672">context</span>: <span style="color:#ae81ff">.</span>
      <span style="color:#f92672">dockerfile</span>: <span style="color:#ae81ff">Dockerfile</span>
    <span style="color:#f92672">volumes</span>:
      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">bind</span>
        <span style="color:#f92672">source</span>: <span style="color:#ae81ff">.</span>
        <span style="color:#f92672">target</span>: <span style="color:#ae81ff">/usr/src/app</span>
      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">volume</span>
        <span style="color:#f92672">target</span>: <span style="color:#ae81ff">/usr/src/app/node_modules</span>
    <span style="color:#f92672">ports</span>:
      - <span style="color:#ae81ff">3333</span>:<span style="color:#ae81ff">3333</span>

  <span style="color:#f92672">db</span>:
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">adonis-demo-db</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:12.2-alpine</span>
    <span style="color:#f92672">ports</span>:
      - <span style="color:#ae81ff">5432</span>:<span style="color:#ae81ff">5432</span>
    <span style="color:#f92672">environment</span>:
      <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">demo_db</span>
      <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">demo</span>
      <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">demo1234</span>

  <span style="color:#f92672">adminer</span>:
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">adonis-demo-adminer</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">adminer:4.7.6</span>
    <span style="color:#f92672">ports</span>:
      - <span style="color:#ae81ff">8080</span>:<span style="color:#ae81ff">8080</span>
</code></pre></div><p>Update the <em>.env</em> file with the Postgres DB config (also change the <code>DB_CONNECTIION</code> to <code>pg</code> for Adonis):</p>
<pre><code>HOST=0.0.0.0
PORT=3333
NODE_ENV=development
APP_NAME=AdonisJs
APP_URL=http://${HOST}:${PORT}
CACHE_VIEWS=false
APP_KEY=LaeQghKnovGDmDnKQlMBIrnjZFUEmyzf

DB_CONNECTION=pg
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USER=demo
DB_PASSWORD=demo1234
DB_DATABASE=demo_db

HASH_DRIVER=bcrypt
</code></pre><p>Check that <em>Postgres</em> and <em>Adminer</em> are started and working by going to <code>http://localhost:8080</code> in the browser and login with the following:</p>
<pre><code>System  : PosgreSQL
Server  : db (the name of the service in docker-compose.yaml)
Username: demo (from POSTGRES_USER: demo)
Password: demo1234 (from POSTGRES_PASSWORD: demo1234)
Database: demo_db (from POSTGRES_DB: demo_db)
</code></pre><p>If the login was sucessful, we can now check that Adonis can connect to the DB by running:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adonis migration:run
</code></pre></div><p>It should say that the Database migrated successfully, and if you refresh <em>Adminer</em> on <code>http://localhost:8080</code> you should now have 3 new tables: <strong>users</strong>, <strong>tokens</strong>, and <strong>adonis_schema</strong> (these migrations were created automatically by the <strong>adonis-cli</strong>).</p>
<p>And now you have a fully functioning <em>AdonisJS API</em> setup with <em>PosgreSQL</em> and <em>Adminer</em>, with easy replication due to <em>Docker</em> and <em>docker-compose</em>.</p>
]]></content>
        </item>
        
        <item>
            <title>Develop an Angular 9 app locally, using Docker</title>
            <link>/posts/angular-docker/</link>
            <pubDate>Fri, 06 Mar 2020 15:00:00 +0200</pubDate>
            
            <guid>/posts/angular-docker/</guid>
            <description>Prerequisites:  node (used here v13.9.0) docker (used here v19.03.5) docker-compose (used here v1.25.4)  Aproximate time to complete this setup: 30 minutes.
Install the Angular CLI globally:
npm i -g @angular/cli Create a new angular project using the CLI and cd into the project directory:
ng new angular-docker-demo cd angular-docker-demo Create the Dockerfile:
# base imageFROMnode:13.8.0# install chrome for protractor testsRUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -RUN sh -c &amp;#39;echo &amp;#34;deb [arch=amd64] http://dl.</description>
            <content type="html"><![CDATA[<h3 id="prerequisites">Prerequisites:</h3>
<ul>
<li>node (used here v13.9.0)</li>
<li>docker (used here v19.03.5)</li>
<li>docker-compose (used here v1.25.4)</li>
</ul>
<p>Aproximate time to complete this setup: 30 minutes.</p>
<p>Install the Angular CLI globally:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">npm i -g @angular/cli
</code></pre></div><p>Create a new angular project using the CLI and cd into the project directory:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">ng new angular-docker-demo
cd angular-docker-demo
</code></pre></div><p>Create the Dockerfile:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Docker" data-lang="Docker"><span style="color:#75715e"># base image</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">FROM</span><span style="color:#e6db74"> node:13.8.0</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># install chrome for protractor tests</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> sh -c <span style="color:#e6db74">&#39;echo &#34;deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main&#34; &gt;&gt; /etc/apt/sources.list.d/google.list&#39;</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> apt-get update <span style="color:#f92672">&amp;&amp;</span> apt-get install -yq google-chrome-stable<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># set working directory</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">WORKDIR</span><span style="color:#e6db74"> /usr/src/app</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># install and cache app dependencies</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">COPY</span> package.json ./package.json<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> npm install<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># copy the source files to the docker image</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">COPY</span> . .<span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># make the host 0.0.0.0 so that the app can</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># be accesed outside of the container</span><span style="color:#960050;background-color:#1e0010">
</span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">CMD</span> npm start -- --host 0.0.0.0<span style="color:#960050;background-color:#1e0010">
</span></code></pre></div><p>Create the .dockerignore file (so that these files are not copied to the image):</p>
<pre><code>node_modules
.git
.gitignore
</code></pre><p>Create the docker image (with the name <strong>angular-docker-demo:latest</strong>) and run it for testing purposes:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker build -t angular-docker-demo .
docker run --rm -it --mount type<span style="color:#f92672">=</span>bind,source<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>PWD<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>,target<span style="color:#f92672">=</span>/usr/src/app --mount type<span style="color:#f92672">=</span>volume,target<span style="color:#f92672">=</span>/app/node_modules -p 4201:4200 angular-docker-demo:latest
</code></pre></div><ul>
<li><code>--rm</code> deletes the container after we are done running it</li>
<li><code>-it</code> allows us to execute shell comands inside the container (behaving like the terminal on our computer)</li>
<li><code>--mount type=bind,source=”\${PWD}”,target=/usr/src/app</code> mounts the Angular app directory as a bind volume in the docker container, allowing us to change the source code in the container on the fly</li>
<li><code>--mount type=volume,target=/app/node_modules</code> mounts the node_modules directory in the container to a separate anonymous volume so that it is not overwritten by the node_modules directory binded in the previous mount from our computer</li>
<li><code>-p 4201:4200</code> publish the 4200 port on which the Angular app is served and bind it to the 4201 port on the computer</li>
</ul>
<p>The app should now be accessible on the computer on the address <code>http://localhost:4201</code>.</p>
<p>Update the <em>karma.conf.js</em> (unit testing config) to use Chrome Headless:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#a6e22e">module</span>.<span style="color:#a6e22e">exports</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">config</span>) {
  <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">set</span>({
    <span style="color:#a6e22e">basePath</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&#39;</span>,
    <span style="color:#a6e22e">frameworks</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;jasmine&#39;</span>, <span style="color:#e6db74">&#39;@angular-devkit/build-angular&#39;</span>],
    <span style="color:#a6e22e">plugins</span><span style="color:#f92672">:</span> [
      <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;karma-jasmine&#39;</span>),
      <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;karma-chrome-launcher&#39;</span>),
      <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;karma-jasmine-html-reporter&#39;</span>),
      <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;karma-coverage-istanbul-reporter&#39;</span>),
      <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;@angular-devkit/build-angular/plugins/karma&#39;</span>),
    ],
    <span style="color:#a6e22e">client</span><span style="color:#f92672">:</span> {
      <span style="color:#a6e22e">clearContext</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span>, <span style="color:#75715e">// leave Jasmine Spec Runner output visible in browser
</span><span style="color:#75715e"></span>    },
    <span style="color:#a6e22e">coverageIstanbulReporter</span><span style="color:#f92672">:</span> {
      <span style="color:#a6e22e">dir</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;path&#39;</span>).<span style="color:#a6e22e">join</span>(<span style="color:#a6e22e">__dirname</span>, <span style="color:#e6db74">&#39;./coverage/angular-docker-demo&#39;</span>),
      <span style="color:#a6e22e">reports</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;html&#39;</span>, <span style="color:#e6db74">&#39;lcovonly&#39;</span>, <span style="color:#e6db74">&#39;text-summary&#39;</span>],
      <span style="color:#a6e22e">fixWebpackSourcePaths</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    },
    <span style="color:#a6e22e">reporters</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;progress&#39;</span>, <span style="color:#e6db74">&#39;kjhtml&#39;</span>],
    <span style="color:#a6e22e">port</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">9876</span>,
    <span style="color:#a6e22e">colors</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    <span style="color:#a6e22e">logLevel</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">LOG_INFO</span>,
    <span style="color:#a6e22e">autoWatch</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    <span style="color:#75715e">// updated &lt;-- HERE
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">browsers</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;ChromeHeadless&#39;</span>],
    <span style="color:#75715e">// new &lt;-- HERE
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">customLaunchers</span><span style="color:#f92672">:</span> {
      <span style="color:#a6e22e">ChromeHeadless</span><span style="color:#f92672">:</span> {
        <span style="color:#a6e22e">base</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Chrome&#39;</span>,
        <span style="color:#a6e22e">flags</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;--no-sandbox&#39;</span>, <span style="color:#e6db74">&#39;--headless&#39;</span>, <span style="color:#e6db74">&#39;--disable-gpu&#39;</span>, <span style="color:#e6db74">&#39;--remote-debugging-port=9222&#39;</span>],
      },
    },
    <span style="color:#a6e22e">singleRun</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span>,
    <span style="color:#a6e22e">restartOnFileChange</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
  });
};
</code></pre></div><p>Check that the unit testing works (the docker container started earlier must still be running — if it&rsquo;s not, run the previous <code>docker run</code> command again):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker exec -it CONTAINER npm run test
</code></pre></div><p>where <code>CONTAINER</code> is the container name or id which you can find by running</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker ps
</code></pre></div><p>Update the <em>protractor.conf.js</em> (e2e testing config) to use Chrome Headless:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-Javascript" data-lang="Javascript"><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">SpecReporter</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;jasmine-spec-reporter&#39;</span>);

<span style="color:#75715e">/**
</span><span style="color:#75715e"> * @type { import(&#34;protractor&#34;).Config }
</span><span style="color:#75715e"> */</span>
<span style="color:#a6e22e">exports</span>.<span style="color:#a6e22e">config</span> <span style="color:#f92672">=</span> {
  <span style="color:#a6e22e">allScriptsTimeout</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">11000</span>,
  <span style="color:#a6e22e">specs</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;./src/**/*.e2e-spec.ts&#39;</span>],
  <span style="color:#a6e22e">capabilities</span><span style="color:#f92672">:</span> {
    <span style="color:#a6e22e">browserName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;chrome&#39;</span>,
    <span style="color:#75715e">// new &lt;-- HERE
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">chromeOptions</span><span style="color:#f92672">:</span> {
      <span style="color:#a6e22e">args</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;--no-sandbox&#39;</span>, <span style="color:#e6db74">&#39;--headless&#39;</span>, <span style="color:#e6db74">&#39;--window-size=1024,768&#39;</span>],
    },
  },
  <span style="color:#a6e22e">directConnect</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
  <span style="color:#a6e22e">baseUrl</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;http://localhost:4200/&#39;</span>,
  <span style="color:#a6e22e">framework</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;jasmine&#39;</span>,
  <span style="color:#a6e22e">jasmineNodeOpts</span><span style="color:#f92672">:</span> {
    <span style="color:#a6e22e">showColors</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
    <span style="color:#a6e22e">defaultTimeoutInterval</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">30000</span>,
    <span style="color:#a6e22e">print</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">function</span>() {},
  },
  <span style="color:#a6e22e">onPrepare</span>() {
    <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;ts-node&#39;</span>).<span style="color:#a6e22e">register</span>({
      <span style="color:#a6e22e">project</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;path&#39;</span>).<span style="color:#a6e22e">join</span>(<span style="color:#a6e22e">__dirname</span>, <span style="color:#e6db74">&#39;./tsconfig.json&#39;</span>),
    });
    <span style="color:#a6e22e">jasmine</span>.<span style="color:#a6e22e">getEnv</span>().<span style="color:#a6e22e">addReporter</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">SpecReporter</span>({ <span style="color:#a6e22e">spec</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">displayStacktrace</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> } }));
  },
};
</code></pre></div><p>Check that the e2e testing works (the docker container started earlier must still be running — if it is not, run the previous <code>docker run</code> command again):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker exec -it CONTAINER npm run e2e -- --port <span style="color:#ae81ff">4202</span>
</code></pre></div><p>If everything works, we can now stop the container — either by pressing <em>Ctrl-C</em> in the container terminal or by running the following command:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker stop CONTAINER
</code></pre></div><p>Create the <em>docker-compose.yaml</em> file in the Angular project’s root:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#34;3.7&#34;</span>
<span style="color:#f92672">services</span>:
  <span style="color:#f92672">web</span>:
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">project-name-web</span>
    <span style="color:#f92672">build</span>:
      <span style="color:#f92672">context</span>: <span style="color:#ae81ff">.</span>
      <span style="color:#f92672">dockerfile</span>: <span style="color:#ae81ff">Dockerfile</span>
    <span style="color:#f92672">volumes</span>:
      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">bind</span>
        <span style="color:#f92672">source</span>: <span style="color:#ae81ff">.</span>
        <span style="color:#f92672">target</span>: <span style="color:#ae81ff">/usr/src/app</span>
      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">volume</span>
        <span style="color:#f92672">target</span>: <span style="color:#ae81ff">/usr/src/app/node_modules</span>
    <span style="color:#f92672">ports</span>:
      - <span style="color:#ae81ff">4201</span>:<span style="color:#ae81ff">4200</span>
</code></pre></div><p>Start docker compose:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker-compose up
</code></pre></div><p>Run tests with:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">docker-compose exec web npm run test
docker-compose exec web npm run e2e -- --port <span style="color:#ae81ff">4202</span>
</code></pre></div><p>And that concludes the setup. I hope that by now you have a good starting point for your next project.</p>
]]></content>
        </item>
        
    </channel>
</rss>
