<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Misc on Techformist</title>
    <link>https://techformist.com/categories/misc/</link>
    <description>Recent content in Misc on Techformist</description>
    <image>
      <url>https://techformist.com/logo.svg</url>
      <link>https://techformist.com/logo.svg</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 18 Nov 2020 06:30:00 +0000</lastBuildDate><atom:link href="https://techformist.com/categories/misc/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Uploading Files in Vue Firestore App</title>
      <link>https://techformist.com/uploading-images-vue-firestore-storage/</link>
      <pubDate>Wed, 18 Nov 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/uploading-images-vue-firestore-storage/</guid>
      <description>&lt;p&gt;Firestore is a super easy way to configure your backend. Firestore provides a range of services anywhere from a database, user authentication, to using machine learning for many use-cases.&lt;/p&gt;
&lt;p&gt;One of the many things you do in a typical app is to enable users to store files. While some find it easier to store files in database (huh?), the most popular option is to store files and reference them in the database record.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Firestore is a super easy way to configure your backend. Firestore provides a range of services anywhere from a database, user authentication, to using machine learning for many use-cases.</p>
<p>One of the many things you do in a typical app is to enable users to store files. While some find it easier to store files in database (huh?), the most popular option is to store files and reference them in the database record.</p>
<p>Firestore provides easy access to a service known as Firebase Storage to store files. Let&rsquo;s see how.</p>
<h2 id="create-vue-view">Create Vue View</h2>
<p>We get started with a simple Vue app (which is using Vuetify, but that does not matter)..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mt-12 pt-md-6&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;title font-weight-black&#34;</span><span class="p">&gt;</span>Image Handler<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mt-12&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-file-input</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;myFile&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">          <span class="na">:rules</span><span class="o">=</span><span class="s">&#34;[rules.maxsize]&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">accept</span><span class="o">=</span><span class="s">&#34;image/png, image/jpeg&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Click to upload file&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">change</span><span class="o">=</span><span class="s">&#34;fileInput&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">:disabled</span><span class="o">=</span><span class="s">&#34;processing&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:append-outer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-progress-circular</span>
</span></span><span class="line"><span class="cl">              <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;processing&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">indeterminate</span>
</span></span><span class="line"><span class="cl">              <span class="na">small</span>
</span></span><span class="line"><span class="cl">            <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-file-input</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;fileUrl&#34;</span> <span class="na">contain</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;fileUrl&#34;</span> <span class="na">max-height</span><span class="o">=</span><span class="s">&#34;500&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span><span class="p">,</span> <span class="nx">mapMutations</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">FirebaseFunctions</span><span class="p">,</span> <span class="nx">FirebaseStorage</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@/services/firebase.js&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">myFile</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">processing</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fileUrl</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="add-a-service-to-invoke-firebase">Add a Service to invoke Firebase</h2>
<p>Install Firebase in your vue app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i firebase
</span></span></code></pre></div><p>Add a service that can be consumed by any Vue components to upload files - <code>src/services/firebase.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">firebase</span> <span class="nx">from</span> <span class="s2">&#34;firebase/app&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;firebase/storage&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// get config from Firebase console
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">firebaseConfig</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">apiKey</span><span class="o">:</span> <span class="s2">&#34;the_api_key&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">authDomain</span><span class="o">:</span> <span class="s2">&#34;myapp.firebaseapp.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">databaseURL</span><span class="o">:</span> <span class="s2">&#34;https://myapp.firebaseio.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">projectId</span><span class="o">:</span> <span class="s2">&#34;myapp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">storageBucket</span><span class="o">:</span> <span class="s2">&#34;myapp.appspot.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">messagingSenderId</span><span class="o">:</span> <span class="s2">&#34;12345&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">appId</span><span class="o">:</span> <span class="s2">&#34;1:123512:web:1abcd23&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">measurementId</span><span class="o">:</span> <span class="s2">&#34;G-123COOL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">firebase</span><span class="p">.</span><span class="nx">apps</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">firebase</span><span class="p">.</span><span class="nx">apps</span><span class="p">.</span><span class="nx">length</span>
</span></span><span class="line"><span class="cl">  <span class="o">?</span> <span class="nx">firebase</span><span class="p">.</span><span class="nx">initializeApp</span><span class="p">(</span><span class="nx">firebaseConfig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">FirebaseStorage</span> <span class="o">=</span> <span class="nx">firebase</span><span class="p">.</span><span class="nx">storage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">firebase</span><span class="p">;</span>
</span></span></code></pre></div><p>We add the above code as a service instead of using them locally in components (or views) since we may need the same code in multiple places.</p>
<p>A few key points to note -</p>
<ul>
<li>Get configuration from your Firebase console. This forms the entirety of <code>firebaseConfig</code> object</li>
<li>We export <code>FirebaseStorage</code> (alongside <code>firebase</code>) and this a ready-to-use service to access the Firebase storage</li>
</ul>
<p>In the next section you will see how we use this service.</p>
<h2 id="add-method-to-upload-file">Add Method to Upload File</h2>
<p>Finally, add the methods to handle image uploads in the view.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">FirebaseStorage</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@/services/firebase.js&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">myFile</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">processing</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fileURL</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">fileInput</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">file</span> <span class="o">&amp;&amp;</span> <span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">processing</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">fr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nx">fr</span><span class="p">.</span><span class="nx">readAsDataURL</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">fr</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;load&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// this is to load image on the UI
</span></span></span><span class="line"><span class="cl">              <span class="c1">// .. not related to file upload :)
</span></span></span><span class="line"><span class="cl">              <span class="k">this</span><span class="p">.</span><span class="nx">fileURL</span> <span class="o">=</span> <span class="nx">fr</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">imgData</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FormData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nx">imgData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;image&#34;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">myFile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="sb">`mypath/</span><span class="si">${</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">metadata</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">contentType</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">myFile</span><span class="p">.</span><span class="nx">type</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">await</span> <span class="nx">FirebaseStorage</span><span class="p">.</span><span class="nx">ref</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">              <span class="p">.</span><span class="nx">child</span><span class="p">(</span><span class="nx">filePath</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">myFile</span><span class="p">,</span> <span class="nx">metadata</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;filePath: &#34;</span><span class="p">,</span> <span class="nx">filePath</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">processing</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>We are doing a few simple things here -</p>
<ul>
<li>We read the file using <code>const fr = new FileReader();</code> and display the file on the UI. This is just a &ldquo;nice to have&rdquo; feature to provide feedback to user when she selects the file</li>
<li>The selected file gets appended to form data - <code>const imgData = new FormData()</code></li>
<li>Upload the file to the path <code>const filePath = ``mypath/${Date.now()}-${file.name}``;</code> on our Firebase storage using <code>FirebaseStorage.ref()</code></li>
</ul>
<p>That is it! You can now upload any file to Firestore (provided your security rules allow you to upload files).</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hide Element from Print in HTML</title>
      <link>https://techformist.com/hide-element-from-print-html/</link>
      <pubDate>Wed, 11 Nov 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/hide-element-from-print-html/</guid>
      <description>&lt;p&gt;You have coded a beautiful page that has a few buttons, some text, a toolbar, a footer with the mandatory copyright statements, and so on. And now your users want to take the print out of the web page and ask you to include that feature.&lt;/p&gt;
&lt;p&gt;The first thing that comes to your mind is to just use the browser feature to print any web page (or to save as PDF). Well, it gets the job done but along with your beautiful, print-worthy content will be the buttons, footer, and what not. So, you get to wonder &amp;ldquo;is it possible to get rid of things only for prints but not on the web page UI&amp;rdquo;? The answer is &amp;ldquo;yes&amp;rdquo;, of course - I am writing a whole post about it.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>You have coded a beautiful page that has a few buttons, some text, a toolbar, a footer with the mandatory copyright statements, and so on. And now your users want to take the print out of the web page and ask you to include that feature.</p>
<p>The first thing that comes to your mind is to just use the browser feature to print any web page (or to save as PDF). Well, it gets the job done but along with your beautiful, print-worthy content will be the buttons, footer, and what not. So, you get to wonder &ldquo;is it possible to get rid of things only for prints but not on the web page UI&rdquo;? The answer is &ldquo;yes&rdquo;, of course - I am writing a whole post about it.</p>
<p>And, a whole post about a couple of simple statements in CSS that can make elements magically disappear.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="nt">print</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">no-print</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">visibility</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>All this will do is to instruct browser to hide any element with class <code>no-print</code> - only while printing the page (that&rsquo;s the <code>@media</code>). This method works independent of frameworks and you can use it to hide elements in Vue, React, et al.</p>
<p>For e.g. consider this page.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      Meet my Friends
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">outlined</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span><span class="p">&gt;</span>Exit<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span>Know More<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in items&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;mt-2 mb-2&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">flat</span>
</span></span><span class="line"><span class="cl">          <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">flat</span> <span class="na">dense</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;transparent&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{item.title}}
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;item.img&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300px&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-footer</span><span class="p">&gt;</span>Images are from Unsplash.<span class="p">&lt;/</span><span class="nt">v-footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The toolbar and footer tag along for the prints.</p>
<p><img loading="lazy" src="/2021/hide-print-element-css-no-css.gif" type="" alt="hide-print-element-css-no-css"  /></p>
<p>Let us change that. Include the simple CSS provided earlier and use the CSS class against all elements that do not need to be printed.</p>
<p>For e.g. we do this to hide the toolbar -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;no-print&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  Meet my Friends
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">outlined</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span><span class="p">&gt;</span>Exit<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span>Know More<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The end result is a cleaner print-out (or PDF) with no unecessary elements.</p>
<p><img loading="lazy" src="/2021/hide-print-element-css-with-css.gif" type="" alt="hide-print-element-css-with-css"  /></p>
<p>Simple and elegant.</p>
<p>Find the code on <a href="https://codepen.io/techformist/pen/ZEpqbwQ">Codepen</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Learning Golang with Fiber</title>
      <link>https://techformist.com/learn-golang-fiber-todo-app/</link>
      <pubDate>Wed, 14 Oct 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/learn-golang-fiber-todo-app/</guid>
      <description>&lt;p&gt;I started with Golang not too long ago, and I loved the fact that I can create a web application with a couple of lines of code. But, as always frameworks help to take that web application to places. Being a practical person who develops apps for side projects and for a living, I cannot simply overstate this fact. A production application is not simply a matter of responding to a &lt;code&gt;hello world&lt;/code&gt; JSON and frameworks take care of the routine tasks of providing structure, connecting to database, enforcing security and so on.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I started with Golang not too long ago, and I loved the fact that I can create a web application with a couple of lines of code. But, as always frameworks help to take that web application to places. Being a practical person who develops apps for side projects and for a living, I cannot simply overstate this fact. A production application is not simply a matter of responding to a <code>hello world</code> JSON and frameworks take care of the routine tasks of providing structure, connecting to database, enforcing security and so on.</p>
<p>From get go, I liked <a href="https://github.com/gofiber/fiber">Fiber framework</a>.</p>
<p><img loading="lazy" src="/2020/golang-fiber_v2_logo.svg" type="" alt="golang-fiber_v2_logo"  /></p>
<p>Fiber is an ExpressJS inspired framework -</p>
<ul>
<li>easy for NodeJS developers to pick up</li>
<li>fast (I mean really fast - see <a href="https://www.techempower.com/benchmarks/">benchmarks</a>)</li>
<li>easy to develop</li>
<li>flexible</li>
</ul>
<p>Coupled with Go&rsquo;s simplicity, light-weight nature, ease of async programming and deployment, Fiber is a formidable (albeit simple) tool to quickly develop web apps.</p>
<p>Also see: <a href="/golang-vs-nodejs-web-apps-developer-view/">a small-scale developer&rsquo;s view of NodeJS vs Golang</a>.</p>
<p>In this post, let us see an example of developing simple CRUD APIs in Fiber v2. We will not be using an ORM or a database to keep things focused on Fiber and Golang.</p>
<h2 id="start-with-go">Start with Go</h2>
<p>First, <a href="https://golang.org/">download and install Go</a> on your computer.</p>
<p>Ensure that your favourite editor (= VSCode) has support to code Go programs.</p>
<ul>
<li><a href="https://marketplace.visualstudio.com/items?itemName=golang.go">Install Go extension</a></li>
<li><a href="https://code.visualstudio.com/docs/languages/go">Configure editor</a></li>
</ul>
<p>If you are looking at this in 2020, you may also need to enable <code>Gopls</code> in VSCode to enable better module support and more modern features. Hit <code>Ctrl + Shift + P</code> &gt; type <code>Settings</code> &gt; Hit <code>Enter</code>. Type in <code>@ext:golang.go gopls</code> to see Go extension settings. Scroll down and enable <code>Go: Use Language Server</code>.</p>
<p>You are all set.</p>
<h2 id="project-structure">Project Structure</h2>
<p>Create a folder on your computer and open that folder in VSCode.</p>
<p>Create a new file <code>server.go</code>. Code in the following -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;hello world&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Open terminal in VSCode (<code>Ctrl + ~</code>) and run program.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">go run server.go
</span></span></code></pre></div><p>You should see an output message if everything works fine.</p>
<h2 id="get-started-on-fiber">Get started on Fiber</h2>
<p>Enter below command in the VSCode terminal to install fiber.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">go get github.com/gofiber/fiber/v2
</span></span></code></pre></div><p>We will now start coding the API in <code>server.go</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2/middleware/logger&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Todo</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">ID</span><span class="w">        </span><span class="kt">int</span><span class="w">    </span><span class="s">`json:&#34;id&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[]</span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">{</span><span class="nx">ID</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;abc&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">{</span><span class="nx">ID</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;def&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fiber</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Use</span><span class="p">(</span><span class="nx">logger</span><span class="p">.</span><span class="nf">New</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">SendString</span><span class="p">(</span><span class="s">&#34;Hello, World!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">app</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">&#34;:5000&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>We have done a few simple things here -</p>
<ul>
<li>defined a basic structure for our todo using <code>type Todo struct {}</code>. We also provided the JSON equivalents of the fields</li>
<li>created a simple array of type <code>Todo</code> - <code>var todos = []Todo{{ID: 1, Name: &quot;abc&quot;, Completed: false},}</code></li>
<li>initiated fiber with <code>app := fiber.New()</code></li>
<li>enabled logging input requests with a single line <code>app.Use(logger.New())</code></li>
<li>enabled a simple response at root using <code>app.Get(&quot;/&quot;, ...)</code></li>
</ul>
<p>Run the program again using <code>go run server.go</code>.</p>
<p>Use a REST client (like <a href="https://insomnia.rest/">Insomnia</a>), invoke your API at <code>http://localhost:5000/</code> to see a <code>hello world</code> response.</p>
<p>You can start seeing similarities with Express server in the simple program - more will follow.</p>
<p>At this stage, you may observe that any change you make will require a restart of server. There are multiple ways of solving this problem including using our dear own <code>nodemon</code>. Let us stick to a Go solution though - we will use a package called <a href="https://github.com/cosmtrek/air">air</a> to monitor for changes in our project folder and restart server whenever there are changes.</p>
<p>In your VSCode terminal, input -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">https://github.com/cosmtrek/air
</span></span></code></pre></div><p>You can stop your own Go server and simply type -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">air
</span></span></code></pre></div><p>The above command will run your server and automatically restart it whenever there are changes.</p>
<p>Onwards then.</p>
<h2 id="pseudo-crud-apis-in-fiber">(Pseudo) CRUD APIs in Fiber</h2>
<p>Let us code in the APIs in our program.</p>
<h3 id="get-request">Get Request</h3>
<p>Introduce below line in <code>main</code> function before <code>app.Listen(&quot;:5000&quot;)</code> line.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">getTodo</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Create a new function below <code>main</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">getTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todos</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The function is (more or less) self-explanatory. The pointer <code>c</code> is fiber context that is injected in our function by fiber framework. We just return an <code>ok</code> with our <code>todos</code> array data. Simple enough.</p>
<p>Test the <code>get</code> API using the URL <code>http://localhost:5000/todo</code> and <code>GET</code> method from the REST client. You will see the below JSON response.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;abc&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;completed&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;def1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;completed&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><h3 id="post-request">Post Request</h3>
<p>Introduce a new line after GET request in <code>server.go</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">postTodo</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>As it was earlier, create a new function called <code>postTodo</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">postTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">type</span><span class="w"> </span><span class="nx">request</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">body</span><span class="w"> </span><span class="nx">request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">BodyParser</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Cannot parse JSON&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">ID</span><span class="p">:</span><span class="w">        </span><span class="nb">len</span><span class="p">(</span><span class="nx">todos</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="p">:</span><span class="w">      </span><span class="nx">body</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="nx">body</span><span class="p">.</span><span class="nx">Completed</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">todos</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusCreated</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>We have accomplished quite a bit, but nothing out of ordinary -</p>
<ul>
<li>specified how our input JSON will look like (<code>request</code> struct)</li>
<li>included a body parser middleware to fetch input JSON data</li>
<li>fetched the incoming data using <code>todo</code> variable</li>
<li>appended <code>todo</code> to our <code>todos</code> array</li>
<li>returned a success message back</li>
</ul>
<p>The below code block takes care of error checks (in our case, this handles parse errors) -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">    </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">BodyParser</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Cannot parse JSON&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>This type of error handling is typical to a Go program. Error checks are handled by an <code>if</code> statement that checks <code>error</code> returned from a previous statement.</p>
<p>Create a POST request in your REST client - <code>http://localhost:5000/todo</code>. Input the following JSON as input -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;xyz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;completed&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Send the request and you will get the same response back. Do a GET request with the same URL and you will see the additional <code>todo</code> record in the response.</p>
<p>Of course, we are dealing only with a variable that stays in memory. The changes will disappear when the server shutsdown or restarts.</p>
<blockquote>
<p>Note: the id generation logic is flaky. We used the length of the array and increment by 1. Ergo, deleting a record can lead to duplicate ids.</p>
</blockquote>
<h3 id="delete-request">Delete Request</h3>
<p>You know the drill by now.</p>
<p>Introduce a new REST service in <code>main</code>..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Delete</span><span class="p">(</span><span class="s">&#34;/todo/:id&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">deleteTodo</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Create a new function..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">deleteTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">paramID</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Params</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">paramID</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Invalid id.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">todo</span><span class="p">.</span><span class="nx">ID</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">todos</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span><span class="o">...</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Record not found&#34;</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>We have used a new function for string type conversion. Include the package in the <code>import</code> statement.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;strconv&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2/middleware/logger&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>A couple of things to take note -</p>
<ol>
<li>We are not manipulating the array directly since the array itself but creating a new array each time there&rsquo;s a change with this line <code>todos = append(todos[0:i], todos[i+1:]...)</code>. Not effective, but works. Instead we could have used a pointer and updated array directly.</li>
<li>We are using a convenient shortcut provided by fiber to create response JSON on the go - <code>fiber.Map{&quot;error&quot;: &quot;Record not found&quot;}</code></li>
</ol>
<h3 id="patch-request">Patch Request</h3>
<p>Patch is similar to <code>delete</code> - except that we change an element instead of removing it.</p>
<p>Include a new function call in <code>main</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Patch</span><span class="p">(</span><span class="s">&#34;/todo/:id&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">patchTodo</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Create the function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">patchTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">type</span><span class="w"> </span><span class="nx">request</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">body</span><span class="w"> </span><span class="nx">request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">BodyParser</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Cannot parse JSON&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">paramID</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Params</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">paramID</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Invalid id.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">todo</span><span class="p">.</span><span class="nx">ID</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">ID</span><span class="p">:</span><span class="w">        </span><span class="nx">id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">Name</span><span class="p">:</span><span class="w">      </span><span class="nx">body</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="nx">body</span><span class="p">.</span><span class="nx">Completed</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Record not found&#34;</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="all-code-in-one-place">All Code in One Place</h3>
<p>We have created a program using Fiber to perform CRUD operations, but on a local variable. With a couple of more packages and a few lines of code we could connect to a DB and get to a more real-world program.</p>
<p>The below code represents the final state. You may also refer to <a href="https://github.com/prashanth1k/demo-todo-fiber-golang">this Git repo</a> for the full code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;strconv&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gofiber/fiber/v2/middleware/logger&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// Todo struct!</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Todo</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">ID</span><span class="w">        </span><span class="kt">int</span><span class="w">    </span><span class="s">`json:&#34;id&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[]</span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">{</span><span class="nx">ID</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;abc&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">{</span><span class="nx">ID</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;def&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fiber</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Use</span><span class="p">(</span><span class="nx">logger</span><span class="p">.</span><span class="nf">New</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">SendString</span><span class="p">(</span><span class="s">&#34;Hello, World!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">getTodo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span><span class="s">&#34;/todo&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">postTodo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/todo/:id&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">getSingleTodo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Delete</span><span class="p">(</span><span class="s">&#34;/todo/:id&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">deleteTodo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Patch</span><span class="p">(</span><span class="s">&#34;/todo/:id&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">patchTodo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">app</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">&#34;:5000&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">getTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todos</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">getSingleTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">paramID</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Params</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">paramID</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;ID invalid.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">todo</span><span class="p">.</span><span class="nx">ID</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Record not found&#34;</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">deleteTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">paramID</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Params</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">paramID</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Invalid id.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">todo</span><span class="p">.</span><span class="nx">ID</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">todos</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="nx">i</span><span class="p">],</span><span class="w"> </span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span><span class="o">...</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Record not found&#34;</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">postTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">type</span><span class="w"> </span><span class="nx">request</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">body</span><span class="w"> </span><span class="nx">request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">BodyParser</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Cannot parse JSON&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">ID</span><span class="p">:</span><span class="w">        </span><span class="nb">len</span><span class="p">(</span><span class="nx">todos</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="p">:</span><span class="w">      </span><span class="nx">body</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="nx">body</span><span class="p">.</span><span class="nx">Completed</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">todos</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">todos</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusCreated</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todo</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">patchTodo</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Ctx</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">type</span><span class="w"> </span><span class="nx">request</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="w">      </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Completed</span><span class="w"> </span><span class="kt">bool</span><span class="w">   </span><span class="s">`json:&#34;completed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">body</span><span class="w"> </span><span class="nx">request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">BodyParser</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Cannot parse JSON&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">paramID</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Params</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">paramID</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Invalid id.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">todo</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">todos</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">todo</span><span class="p">.</span><span class="nx">ID</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">Todo</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">ID</span><span class="p">:</span><span class="w">        </span><span class="nx">id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">Name</span><span class="p">:</span><span class="w">      </span><span class="nx">body</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">Completed</span><span class="p">:</span><span class="w"> </span><span class="nx">body</span><span class="p">.</span><span class="nx">Completed</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">todos</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nf">Status</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">).</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">Map</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Record not found&#34;</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Now go create your magic. Enjoy Go!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Golang vs. NodeJS for Web Apps - A Small-scale Developer View</title>
      <link>https://techformist.com/golang-vs-nodejs-web-apps-developer-view/</link>
      <pubDate>Wed, 07 Oct 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/golang-vs-nodejs-web-apps-developer-view/</guid>
      <description>&lt;p&gt;If you had enough of single threaded behaviour of Node and are ready for the next level (/s) - I strongly recommend you evaluate Go for your next project.&lt;/p&gt;
&lt;p&gt;Javascript is the most used language in the world and that status will not change in a hurry - thanks to its frontend nature. NodeJS is super useful since we can use the same language for server and stand up a production grade server using frameworks like ExpressJS or Fastify in a matter of hours. I absolutely am in love with NodeJS -&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you had enough of single threaded behaviour of Node and are ready for the next level (/s) - I strongly recommend you evaluate Go for your next project.</p>
<p>Javascript is the most used language in the world and that status will not change in a hurry - thanks to its frontend nature. NodeJS is super useful since we can use the same language for server and stand up a production grade server using frameworks like ExpressJS or Fastify in a matter of hours. I absolutely am in love with NodeJS -</p>
<ol>
<li>Light weight</li>
<li>Easy to create, deploy and maintain applications</li>
<li>Many, many packages that make it easy to add new functionality (yes, I love them packages)</li>
<li>Decent performance</li>
</ol>
<p>However, there are a few concerns -</p>
<ol>
<li>Single threaded nature can be a cause for concern at some level. Yes, there are promises but with due respect to them - there are going to be transactions that can still make the engine &ldquo;stuck&rdquo;. CPU-bound, high performance tasks can be a problem in Node</li>
<li>Treatment of numbers as floats are perceived to be harmful for applications that treat such transactions as sacred</li>
<li>Types help in development-time and runtime validations that avoid all that additional code checks. Medium to high complexity projects can be (arguably?) easier to maintain with typed languages.</li>
</ol>
<p>I am a small-scale developer + entrepreneur who creates a number of SaaS applications and also builds applications for my clients. I do not quite share any of the outlined concerns of Javascript, but I was actively looking at an alternate &ldquo;fast to develop&rdquo; platform about an year back. That led me to do some quick research by developing &ldquo;to do&rdquo; apps like there was no tomorrow. Here&rsquo;s a highly opinionated view of a small-scale entrepreneur and developer on how Golang can be super useful as part of your tool-set.</p>
<blockquote>
<p>Aside: While I use Typescript in many projects, I cannot firmly switch over to that camp. I love the &ldquo;type less&rdquo; nature of JS and it keeps my productivity high for smaller projects. Also, I found it easier to switch all the way to C# or Go.</p>
</blockquote>
<h2 id="the-alternatives">The Alternatives</h2>
<p>The alternatives I evaluated were -</p>
<ol>
<li>Rust: Seems to be everyone&rsquo;s darling nowadays, but is hard to learn and is certainly not a &ldquo;highly productive&rdquo; language for something like web development</li>
<li>ASP.NET and C#: I love the way C# has been developed over the past many years. I have used C# only for amateurish desktop applications, and am not afraid of extending it to web. Web assembly support and Blazor make this an exciting space</li>
<li>Golang: Simple to learn, code and deploy (thanks to the highly touted &ldquo;single executable&rdquo; nature of Go). Really light-weight and quite exciting async programming experience</li>
</ol>
<p>Finally what drew me to use Go was the &ldquo;promise&rdquo; of easy async programming using go routines and the promise of better &amp; consistent performance. Though I doubt whether I can put either of these to active / practical use and see exponential gains for any of my applications in the near term.</p>
<p><img loading="lazy" src="/2020/golang-gopher.png" type="" alt="golang-gopher"  /></p>
<h2 id="advantage-go-for-web-applications">Advantage Go for web applications</h2>
<p>Here&rsquo;s a quick take on why you should consider Go in your toolset for web development.</p>
<h3 id="simplicity">Simplicity</h3>
<p>Go lang is easy to learn.</p>
<p>Despite the strictly typed nature of Go and more complex concepts like channels, coroutines, et. al, one can easily start identifying everything that a given program is trying to do quite easily. I found this easy to pick up nature quite invaluable when trying to evaluate the complexity of web applications which were created by others but I had to go in and make changes.</p>
<h3 id="light-weight">Light weight</h3>
<p><img loading="lazy" src="/2020/light-weight-apps-golang.jpg" type="" alt="light-weight-apps-golang.jpg"  /></p>
<p>Yes, Go is as light-weight as they come despite being &ldquo;decently productive&rdquo;.</p>
<p>I can run more production applications, even more POCs and then some on a single VPS without any problems. While I did not quite have any issues per se with NodeJS based apps, I would love to imagine a not-so-distant future where I can deploy hundreds of smaller applications without spending a fortune using Go.</p>
<h3 id="performance">Performance</h3>
<p>I am not going to lie. Despite reading up quite a bit and finding for myself on how performance of applications can be specific to a given use case, the promise of high performance in Go applications is a strong one.</p>
<p><img loading="lazy" src="/2020/speed-performance-application.jpg" type="" alt="speed-performance-application"  /></p>
<p>After looking at <a href="https://www.techempower.com/benchmarks/">Techempower benchmarks</a> for a few years, I had resigned to the fact that my favourite frameworks are not going to be burning rubber on the road anytime soon. Go (and to a certain extent C#) is one of the ways of &ldquo;future-proofing&rdquo; my products and re-assure to myself that I can build &ldquo;fast applications&rdquo; for thousands of users.</p>
<p>Do note -</p>
<ul>
<li>often times, performance is a concept highly specific to your use case</li>
<li>using a language does not ensure automatic high-performance, but the language features like async programming, &ldquo;ability to make the machine work to its fullest&rdquo; help. Compiled languages are often better here</li>
<li>Web applications often do not have the same issues as their desktop / mobile counterparts. For e.g. Fastify + Postgres ranks higher (#148) as compared to ASP.NET + Entityframework Core + Postgres (#149!). Using an ORM in Fastify can of course throw a spanner in these results. A highly productive framework like Nest + Fastify + Mysql is not far behind (#189)</li>
</ul>
<p><em>* Performance rankings are good as of 2020</em></p>
<h3 id="scalability">Scalability</h3>
<p>Go has lower memory footprint. It also compiles to a specific platform and occupies lower space (the latter is not quite an evaluation factor in this day and age). For me low memory usage translates to better scalability.</p>
<p>Concurrency and the ease of using async programming also contributes to better scalability. You could potentially use Go routines to start hundreds or thousands of threads on a 1 GB VPS without issues.</p>
<h3 id="developer-friendly">Developer-friendly</h3>
<p>Go is easy to learn, comparatively easy to develop, and easy to deploy. Go also features in-built code formatting rules, good support in code editors and a good packaging system.</p>
<h2 id="the-bad-of-golang">The Bad of Golang</h2>
<p>While I would love to use Go for command line applications, I am not too sure I will switch over my web apps really soon.</p>
<ol>
<li>There&rsquo;s a lot of typing/copy-paste involved. Go&rsquo;s simplicity also means fewer functions that provide good abstractions for common tasks. While I would love to use <code>for</code> loops for every problem, Javascript and C# have really good shortcuts to solve problems that are too elegant and productive to resist</li>
<li>I don&rsquo;t particularly like the error handling (<code>if err != nil</code>). I grew up thinking the world of <code>try/catch</code> - so this may be an &ldquo;old-man problem&rdquo;</li>
<li>Golang was blessed on us a while back, but there are far few gigs on Go. SMBs stay away from the Go world since they don&rsquo;t see enough developers available (= expensive to create and maintain). This is a problem for my &ldquo;develop everything despite severely limited resource&rdquo; mentality</li>
</ol>
<p>I could not avoid a direct performance to NodeJS and the thousands of frameworks therein.</p>
<ul>
<li>Node was more productive - thanks to Javascript, and many useful packages in the Node ecosystem</li>
<li>Node frameworks offer &ldquo;more&rdquo;. I can completely switch to Typescript and code like Angular in NestJS, or go kaput with my own structure using Express/Fastify</li>
<li>Examples, demos and learning resources are bountiful in NodeJS. For e.g. integrating your favourite payment gateway is made easier with examples and possibly a SDK for NodeJS, not so for Go (though you could use their APIs as easily)</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<ul>
<li>Learn Go</li>
<li>Use Go for side projects</li>
<li>Don&rsquo;t completely switch over (unless of course if you are in a Go-friendly company)</li>
</ul>
<p>Are you sold and want to try out Go?</p>
<ul>
<li>Start with the &ldquo;<a href="https://tour.golang.org/welcome/1">tour of Go</a>&rdquo;</li>
<li>Once you are ready, start with <a href="https://github.com/gofiber/fiber">Fiber framework</a>. While frameworks are not as important in Go as in Node, Fiber makes transition easy since it follows the same concepts as ExpressJS</li>
</ul>
<p>Also see: <a href="/learn-golang-fiber-todo-app">start on golang with Fiber</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Shorten URL with Express and AlpineJS</title>
      <link>https://techformist.com/shorten-url-app-express-alpinejs/</link>
      <pubDate>Wed, 01 Jul 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/shorten-url-app-express-alpinejs/</guid>
      <description>&lt;p&gt;In this post let us see how we can leverage the power of Express with a sprinkling of Alpine JS to create a quick URL shortener application.&lt;/p&gt;
&lt;h2 id=&#34;but-why&#34;&gt;But, why?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://expressjs.com/&#34;&gt;Express&lt;/a&gt; is the most popular server-side framework and my &amp;ldquo;go to&amp;rdquo; choice for creating anything really quick. Building a front-end for Express is as easy as using handlebars (or anything really) that goes in HTML served by Express. But that lacks a &amp;ldquo;nice&amp;rdquo; user experience.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this post let us see how we can leverage the power of Express with a sprinkling of Alpine JS to create a quick URL shortener application.</p>
<h2 id="but-why">But, why?</h2>
<p><a href="https://expressjs.com/">Express</a> is the most popular server-side framework and my &ldquo;go to&rdquo; choice for creating anything really quick. Building a front-end for Express is as easy as using handlebars (or anything really) that goes in HTML served by Express. But that lacks a &ldquo;nice&rdquo; user experience.</p>
<p><a href="https://github.com/alpinejs/alpine">AlpineJS</a> is a small and sweet framework that will go places. It follows Vue in many respects (and tries to achieve parity in a lot of respects), but does not want to be a full-scaled framework. Rather, it sits happily with other code to provide user interactivity to your front-end.</p>
<p>Depending on what you are set to do, AlpineJS can be a good choice for your back-end application.</p>
<h2 id="use-case-shorten-url">Use Case: Shorten URL</h2>
<p>The premise is simple.</p>
<ol>
<li>User supplies URL (e.g. <code>http://twitter.com/techformist</code>)</li>
<li>We shorten it and give it back to user (e.g. <code>https://go.co/tf</code>, assuming <code>go.co</code> is our domain)</li>
<li>Anyone can use the short URL. The request just bounces off our server to navigate to the longer web address</li>
</ol>
<p>For simplicity we will ignore any authentication requirements.</p>
<p>We will use ExpressJS as the backend because that is one of the quicker and nicer frameworks to work with. We will use SQLite as the database.</p>
<h2 id="create-a-basic-express-app-with-sqlite-db">Create a Basic Express App with SQLite DB</h2>
<p>We will start building with some basic code. No generators, no fluff.</p>
<p>Create a new folder for your project and initialise using <code>npm</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">mkdir</span> shortu
</span></span><span class="line"><span class="cl"><span class="k">cd</span> shortu
</span></span><span class="line"><span class="cl">npm init -y
</span></span></code></pre></div><p><code>npm init</code> will create <code>package.json</code> in your project folder. Edit the file to include scripts to run your application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//  ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;run&#34;</span><span class="p">:</span> <span class="s2">&#34;node server&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;nodemon server&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Install <code>express</code>, <code>sqlite3</code> and a few useful libraries for security.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save express express cors helmet sqlite3 yup
</span></span></code></pre></div><p>Install <code>nodemon</code> so that our express application gets restarted automatically every time there is a change. This is useful during development.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save-dev nodemon
</span></span></code></pre></div><p>Create a new file <code>server.js</code> - this will be our main Express file. Let us bring in <code>express</code> and friends.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;express&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">cors</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;cors&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">helmet</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;helmet&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">yup</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;yup&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Include logic to initialise database.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">sqlite3</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;sqlite3&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">db</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">sqlite3</span><span class="p">.</span><span class="nx">Database</span><span class="p">(</span><span class="s2">&#34;./db/data.sqlite&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Connected to the SQLite database.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;CREATE TABLE IF NOT EXISTS urls (slug VARCHAR(100) PRIMARY KEY, url VARCHAR(255), clicks INTEGER)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nx">res</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="nx">next</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>SQLite is just a file at heart (which database isn&rsquo;t?). The above logic checks for a file called <code>db/data.sqlite</code>. If the file does not exist, SQLite takes care of creating a new file. <code>db.run</code> is used to run a SQL statement.</p>
<p>We will execute an SQL to create a table called <code>urls</code> if it doesn&rsquo;t already exist. There are three columns in this table -</p>
<ul>
<li><code>url</code> - long form URL supplied by user</li>
<li><code>slug</code> - key part of short-form URL. Can be supplied by user or we generate one</li>
<li><code>clicks</code> - no. of clicks on the short URL</li>
</ul>
<p>Initialise express.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// middleware
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">helmet</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">cors</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="kr">static</span><span class="p">(</span><span class="s2">&#34;./public&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// listener
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Listening on port 3000.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Include a test statement to show a message for a request.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">&#34;/*&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;Hello, world&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>.. and an error function to process any errors anywhere.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// error handling
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">err</span><span class="p">,</span> <span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">500</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span> <span class="nx">message</span><span class="o">:</span> <span class="nx">err</span><span class="p">.</span><span class="nx">message</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The basic Express application is now ready!</p>
<p>You can run the application using -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">npm</span> <span class="nx">run</span> <span class="nx">dev</span>
</span></span></code></pre></div><p>This should print a message - <code>Listening on port 3000.</code></p>
<p>Send a <code>POST</code> request using your <a href="https://insomnia.rest/">favourite API testing tool</a> to see <code>&quot;Hello, world&quot;</code> message in response.</p>
<h2 id="define-apis-in-your-express-application">Define APIs in your Express Application</h2>
<p>Before we go ahead with the API request/response, let use define a valid structure for the expected input.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// schema defn.
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">schema</span> <span class="o">=</span> <span class="nx">yup</span><span class="p">.</span><span class="nx">object</span><span class="p">().</span><span class="nx">shape</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">url</span><span class="o">:</span> <span class="nx">yup</span><span class="p">.</span><span class="nx">string</span><span class="p">().</span><span class="nx">url</span><span class="p">().</span><span class="nx">required</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">  <span class="nx">slug</span><span class="o">:</span> <span class="nx">yup</span><span class="p">.</span><span class="nx">string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>We will have three services -</p>
<ol>
<li>Create new &ldquo;short URL&rdquo;</li>
<li>Navigate user to the long URL when a valid short URL is provided</li>
<li>List existing short URLs</li>
</ol>
<p>This is also a good time to remember that we need to generate a random string if user does not provide the &ldquo;slug&rdquo; for the short URL. This can be done in a number of ways.</p>
<p>We will use a package called <code>crypto-random-string</code>. Let us install the package.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save crypto-random-string
</span></span></code></pre></div><p>Alternatively, you can use -</p>
<ul>
<li><a href="https://www.npmjs.com/package/nanoid">nanoid</a></li>
<li><a href="https://www.npmjs.com/package/randomstring">randomstring</a></li>
</ul>
<p>Let us start coding in the services.</p>
<h3 id="create-new-short-url">Create new &ldquo;short URL&rdquo;</h3>
<p>Create a new short URL for the user-provided URL.</p>
<ol>
<li>The generated short URL will be the combination of our domain (where the Express application is running) + a short slug.</li>
<li>Slug is provided in request. It is optional, generate a URL-safe slug if not provided</li>
<li>Store long form URL and the slug in the database</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">&#34;/new&#34;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// get url and slug from input
</span></span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="p">{</span> <span class="nx">url</span><span class="p">,</span> <span class="nx">slug</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;req.body: &#34;</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">validate</span><span class="p">({</span> <span class="nx">url</span><span class="p">,</span> <span class="nx">slug</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">slug</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// generate slug if not provided by user
</span></span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">cryptoRandomString</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;crypto-random-string&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">slug</span> <span class="o">=</span> <span class="nx">cryptoRandomString</span><span class="p">({</span> <span class="nx">length</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;url-safe&#34;</span> <span class="p">}).</span><span class="nx">toLowerCase</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// insert everything in DB
</span></span></span><span class="line"><span class="cl">    <span class="nx">db</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="sb">`INSERT INTO urls(url, slug , clicks) VALUES(&#39;</span><span class="si">${</span><span class="nx">url</span><span class="si">}</span><span class="sb">&#39;, &#39;</span><span class="si">${</span><span class="nx">slug</span><span class="si">}</span><span class="sb">&#39;, 0)`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;err: &#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="nx">next</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// response message has the short URL
</span></span></span><span class="line"><span class="cl">        <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">shorturl</span><span class="o">:</span> <span class="sb">`http://</span><span class="si">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">host</span><span class="si">}</span><span class="sb">/</span><span class="si">${</span><span class="nx">slug</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">slug</span><span class="o">:</span> <span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">url</span><span class="o">:</span> <span class="nx">url</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">next</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>It is time to test this out!</p>
<p>Send a request to <code>http://localhost:3000/new</code> like so -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://google.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;ro0wgfvwxg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You should receive this beautiful response -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://google.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;ro0wgfvwxg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;shorturl&#34;</span><span class="p">:</span> <span class="s2">&#34;http://localhost:3000/ro0wgfvwxg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Your short URL generation is ready, onwards to doing something with this request.</p>
<h3 id="navigate-user-to-long-url">Navigate user to long URL</h3>
<p>With the previous service the user received this short URL <code>http://localhost:3000/ro0wgfvwxg</code>, which can now be happily shared by her to thousands of people.</p>
<p>When these people use the URL in a browser, the request is sent to our server. This can be fulfilled by a service that will fetch the long-form URL from the given short URL, and automatically navigate users to the correct destination.</p>
<p>This is easier than it looks.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;/:id&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// id = slug. e.g. `ro0wgfvwxg`
</span></span></span><span class="line"><span class="cl">  <span class="c1">// fetch data from DB
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="sb">`SELECT * FROM urls where slug=&#39;</span><span class="si">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb">&#39;`</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// navigate user to the right URL
</span></span></span><span class="line"><span class="cl">    <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The above code should do the trick. But, let&rsquo;s improve that a bit more -</p>
<ul>
<li>add error handling - navigate user to an error page if slug is not found</li>
<li>add logic to count number of clicks. Each time anyone uses the URL, the number of clicks will be incremented</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;/:id&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="sb">`SELECT * FROM urls where slug=&#39;</span><span class="si">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb">&#39;`</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="nx">next</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">data</span><span class="p">)</span> <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="sb">`/?error=</span><span class="si">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb"> not found!`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// add clicks
</span></span></span><span class="line"><span class="cl">    <span class="nx">db</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="sb">`UPDATE urls SET clicks = clicks + 1 WHERE slug=&#39;</span><span class="si">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb">&#39;`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Now, try navigating to <code>https://localhost:/ro0wgfvwxg</code> in the browser. You should be navigated to the correct URL <code>http://google.com</code>.</p>
<h3 id="list-urls">List URLs</h3>
<p>Let us code the last of the services - list all URLs in the database.</p>
<p>This service is similar to the previous one, except that we run a simpler query to fetch all URLs and return them to the caller.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;/list&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span><span class="sb">`SELECT * FROM urls`</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="nx">next</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">data</span><span class="o">:</span> <span class="nx">data</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Try it! A request to <code>https://localhost:3000/list</code> should return all the URLs from the database.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;ro0wgfvwxg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://google.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;clicks&#34;</span><span class="p">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;slug&#34;</span><span class="p">:</span> <span class="s2">&#34;whphznmvcf&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://bing.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;clicks&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Your backend application is now fully ready to take on the world.</p>
<h2 id="front-end-for-shorten-url-application">Front-end for Shorten URL Application</h2>
<p>Create a simple HTML file <code>index.html</code> in the project root, which will contain the whole of our frontend. Include AlpineJS from CDN.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=400px, initial-scale=1.0&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Shortu<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span>
</span></span><span class="line"><span class="cl">      <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">defer</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    Frontend Does Not Rock
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Let us code some CSS since no one likes to see default styling. Since I am too lazy to do the complete styling by myself (capability aside), I will use a simple CSS framework called &ldquo;MVP.css&rdquo;.</p>
<p>Include MVP.css and a custom CSS (for minimal style changes) in the HTML head.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/mvp.css&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;./assets/custom.css&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>Create folders in the project root.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">mkdir</span> public <span class="p">&amp;&amp;</span> <span class="k">cd</span> public <span class="p">&amp;&amp;</span> <span class="k">mkdir</span> assets
</span></span><span class="line"><span class="cl"><span class="k">cd</span> assets
</span></span><span class="line"><span class="cl">touch custom.css
</span></span></code></pre></div><p>We can use powerful functions to bind HTML elements to backend. Let us create a form and the necessary objects that will act as the container for all functionality.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">x-data</span><span class="o">=</span><span class="s">&#34;process()&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;title&#34;</span><span class="p">&gt;</span>Shortu URL Shortener<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;subtitle&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    Input the long URL, hit &#34;Shorten&#34; and see magic happen.
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">form</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;input-group&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Provide the long URL&#34;</span> <span class="na">x-model</span><span class="o">=</span><span class="s">&#34;url&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">      <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;form-control&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Desired short url phrase (optional)&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">x-model</span><span class="o">=</span><span class="s">&#34;slug&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">process</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">url</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">slug</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Note that -</p>
<ul>
<li><code>process()</code> will provide the infrastructure for AlpineJS. You can define JS variables, functions and more within <code>process</code> function</li>
<li><code>x-model</code> binds the HTML element to the backend variable (for e.g. <code>url</code>). This is similar to <code>v-model</code> in Vue</li>
</ul>
<p>Express serves the HTML file by default when we navigate to <code>https://localhost:3000/</code>. Navigate to this URL in the browser to see your application in action.</p>
<p>Let us add functionality to allow user to send request to Express and see results.</p>
<p>Add a button that sends data to backend Express, and elements to show results (or error). Input below code before the ending <code>&lt;/form&gt;</code> element.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span> <span class="na">x-on:click</span><span class="err">.</span><span class="na">prevent</span><span class="o">=</span><span class="s">&#34;shortenURL()&#34;</span><span class="p">&gt;</span>Submit<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">x-show</span><span class="o">=</span><span class="s">&#34;error&#34;</span> <span class="na">x-on:click</span><span class="o">=</span><span class="s">&#34; error = &#39;&#39;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">span</span> <span class="na">x-text</span><span class="o">=</span><span class="s">&#34;error&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;error&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>❌
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;results&#34;</span> <span class="na">x-show</span><span class="o">=</span><span class="s">&#34;data[&#39;shorturl&#39;]&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;results&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;label&#34;</span><span class="p">&gt;</span>Your short URL:<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h3</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;display: inline-block;&#34;</span> <span class="na">x-text</span><span class="o">=</span><span class="s">&#34;data.shorturl&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Add the corresponding functions and variables in <code>process</code>.</p>
<ul>
<li><code>data</code> to store the response URL list and <code>error</code> to store error</li>
<li><code>shortenURL</code> - a function that invokes our <code>new</code> service to shorten a given URL</li>
</ul>
<p>The complete <code>&lt;script&gt;</code> element is below -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">process</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">error</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">data</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">url</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">slug</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">shortenURL</span><span class="o">:</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;this.url&#34;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`/new/`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">method</span><span class="o">:</span> <span class="s2">&#34;POST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nx">Accept</span><span class="o">:</span> <span class="s2">&#34;application/json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;Content-Type&#34;</span><span class="o">:</span> <span class="s2">&#34;application/json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">body</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">url</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">url</span><span class="p">,</span> <span class="nx">slug</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">slug</span> <span class="p">}),</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">resJson</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">result</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;resJson: &#34;</span><span class="p">,</span> <span class="nx">resJson</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;result.ok: &#34;</span><span class="p">,</span> <span class="nx">result</span><span class="p">.</span><span class="nx">ok</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="nx">resJson</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;in error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">resJson</span><span class="p">[</span><span class="s2">&#34;message&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;data: &#34;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;error: &#34;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;error&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Voila, your application is ready just like that.</p>
<p><img loading="lazy" src="/2020/url-shortener-express-alpinejs.gif" type="" alt="url-shortener-express-alpinejs"  /></p>
<p>Complete code is at <a href="https://github.com/techformist/shortu">this Github repo</a>.</p>
<h2 id="the-end">The End</h2>
<p>Building applications with Express is fun, and libraries like Alpine make the user experience fun too. I have to say - I am quite smitten this smart library.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Migrating Wordpress Sites Across Domains</title>
      <link>https://techformist.com/wordpress-migration-domains/</link>
      <pubDate>Wed, 24 Jun 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/wordpress-migration-domains/</guid>
      <description>&lt;p&gt;I recently had to pick up an old project on Wordpress and deploy the site on a CPanel server. So, I thought it was just the right time to document the steps needed to migrate Wordpress site from one host to the other, or from one domain to the other. While there are references to CPanel, the migration is almost the same for any control panels (or even when you don&amp;rsquo;t use one).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I recently had to pick up an old project on Wordpress and deploy the site on a CPanel server. So, I thought it was just the right time to document the steps needed to migrate Wordpress site from one host to the other, or from one domain to the other. While there are references to CPanel, the migration is almost the same for any control panels (or even when you don&rsquo;t use one).</p>
<h2 id="the-art-of-wordpress-site-migration">The Art of Wordpress Site Migration</h2>
<p>It may be just me but I don&rsquo;t quite understand why Wordpress site migration has to be so &ldquo;difficult&rdquo; for a typical user. Note that I said &ldquo;difficult&rdquo; as compared to other user-facing, &lsquo;citizen-admin friendly&rsquo; platforms. It is indeed cake-walk when compared to typical developer-owned tasks. I may not exactly an expert to deep dive and show why things are as they are but, there are more than few ways of making the process easy.</p>
<p>Before we go further on the topic, let&rsquo;s look at typical use cases for migrating Wordpress sites -</p>
<ol>
<li>Migrate sites from one host to the other
The movement of sites from one host to the other result in different database names, connection parameters, and file structures.</li>
<li>Migrate sites from one domain to the other
This refers to moving site from one domain (say example.com) to a new domain (newexample.com), or migrating from a test to a production system (example.test to example.com).</li>
</ol>
<p>We don&rsquo;t have cover all use cases to keep this post sane, instead we focus on &ldquo;typical&rdquo; migration cases that one (me!) is most likely to do.</p>
<p>Here are the typical questions when migrating sites -</p>
<ol>
<li>Which components need to be migrated?</li>
<li>How should we move data/files/other?</li>
<li>What options do I have for Wordpress site migration, and which is the best option?</li>
</ol>
<p>I fussed with such questions over years, used different workflows, and finally, developed one plugin-independent flow that simply works. The workflow may not be optimal (let me know!), or may be even wrong - but it does get the work done. So, YMMV.</p>
<p>This flow is more geared towards people who are comfortable working with databases, applications and so forth. Here are the pre-requisites -</p>
<ul>
<li>Database created in the target environment. User id /password and relevant host details are available to you</li>
<li>File system access (e.g. through an explorer in CPanel) available to you</li>
</ul>
<h2 id="components-for-migration">Components for Migration</h2>
<p>Wordpress is based on PHP, and like many PHP applications, is quite simple at heart. We primarily deal with two components -</p>
<ol>
<li>Files</li>
<li>Database</li>
</ol>
<h4 id="files">Files</h4>
<p>Files reside in <code>wp-content</code> folder within Wordpress root installation folder. They may include -</p>
<ol>
<li>User data including images, video and other media, data files that you may be using in your site, files made available for download, and other user generated files.</li>
<li>Site customisation files - themes, styles, etc.</li>
</ol>
<h4 id="database">Database</h4>
<p>Export database from the source and import in the target database.</p>
<p>You can change the database to reflect the new site in a one of the below ways -</p>
<ol>
<li>Change strings in export file before importing content in target</li>
<li>Run SQL in target database after the import</li>
<li>Run a PHP script that will do the updates for you</li>
</ol>
<h2 id="workflow-for-wordpress-site-migration">Workflow for Wordpress Site Migration</h2>
<p>This workflow is generic in nature and works for typical migration use cases -</p>
<ol>
<li>Moving sites from one host to the other</li>
<li>Moving sites across environments (e.g. from test to production)</li>
</ol>
<h4 id="copy-files">Copy Files</h4>
<p>All you are expected to do is copy the entire Wordpress folder from the source to the destination. You could also copy/paste <code>wp-content</code> on the same versions of Wordpress with similar configuration.</p>
<p>Once the files are copied over, you do a few changes -</p>
<ol>
<li>
<p>Update configuration (for e.g. in <code>wp-config</code>) to point to the right database. Your old file will have the older database details. Change -</p>
<ul>
<li>Database host and/or database name (CPanel may enforce naming convention of prefixing table names with your root domain. e.g. <code>techformist_wp_options</code>)</li>
<li>User id / password</li>
</ul>
</li>
<li>
<p>Revert <code>.htaccess</code> file in the root folder to the <a href="https://wordpress.org/support/article/htaccess/">default</a>. You can also regenerate this file from within Wordpress. In its simplest form, <code>.htaccess</code> looks like the below.</p>
<pre tabindex="0"><code># BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress
</code></pre></li>
</ol>
<h2 id="export-and-import-database">Export and import database</h2>
<p>We do this database change to migrate Wordpress sites across domains. If you have the exact same domain (with HTTPS) - skip this step.</p>
<p>Login to your source environment. Export database to a file in source.</p>
<p>If you are using CPanel, go to <code>phpMyAdmin</code> from your control panel home. Select your database and go to <code>Export</code> tab.</p>
<p><img loading="lazy" src="/2020/wordpress-migration-export-database-cpanel.jpg" type="" alt="wordpress-migration-export-database-cpanel"  /></p>
<p>Defaults will work for most cases, just click <code>Go</code> to export database to a file. This will export the table structure and data.</p>
<p>If you are migrating to an existing database and want to overwrite target with data from the source, explore the options to delete and re-create tables -</p>
<ul>
<li>select the <code>Export method</code> as <code>Custom - display all possible options</code></li>
<li>then select <code>Add DROP DATABASE IF EXISTS statement</code> option.</li>
</ul>
<p>You can import the file in target database as-is, but it may make sense to change the export file before you import. This is particularly useful if you are changing your domain name or migrating from test to production (also a domain name change).</p>
<ol>
<li>Open the file in an editor like Notepad++</li>
<li>Hit <code>Find and Replace</code></li>
<li>Find for all instances of <code>oldurl</code> and replace them with <code>newurl</code></li>
<li>If the new url is not setup yet, it is a good idea to replace with <code>http</code> version of the URL rather than using <code>https</code></li>
</ol>
<p>To import the file -</p>
<ol>
<li>Logon to your <code>phpMyAdmin</code> on target host</li>
<li>Navigate to <code>Import</code> tab</li>
<li>Select the file to import and hit <code>Go</code></li>
</ol>
<h2 id="optional-updating-database-without-file-modifications">[Optional] Updating database without file modifications</h2>
<p>Again, this step is relevant only if you are changing domain names.</p>
<p>If you cannot / do not want to work with files, you can always work with SQL to update database to work in your new setup.</p>
<p>First, import the files in target database using <code>phpMyAdmin</code> or similar tools.</p>
<p>In <code>phpMyAdmin</code>, go to <code>SQL</code> tab. Paste the following SQLs .</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">wp_options</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">option_value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">replace</span><span class="p">(</span><span class="n">option_value</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;oldurl&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;newurl&#39;</span><span class="p">)</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">option_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;home&#39;</span><span class="w"> </span><span class="k">OR</span><span class="w"> </span><span class="n">option_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;siteurl&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">wp_posts</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">guid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">replace</span><span class="p">(</span><span class="n">guid</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;oldurl&#39;</span><span class="p">,</span><span class="s1">&#39;newurl&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">wp_posts</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">post_content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">replace</span><span class="p">(</span><span class="n">post_content</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;oldurl&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;newurl&#39;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">COMMIT</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>Replace <code>oldurl</code> and <code>newurl</code> with your relevant URLs from the source and target systems. Hit <code>Go</code>.</p>
<h2 id="finishing-up">Finishing up</h2>
<p>Provision SSL certificates for your target site. Once again, this is made easy by control panels.</p>
<p>In CPanel -</p>
<ol>
<li>Go to <code>Let's Encrypt</code> link on the home page of control panel</li>
<li>Select <code>Install Certificate</code> against your domain</li>
</ol>
<p>In a VPS just install Certbot, and it will do the rest for you :).</p>
<p>Update your site to use <code>https</code>-</p>
<ol>
<li>Login to <code>&lt;yourdomain&gt;/wp-admin</code></li>
<li>Go to <code>Settings</code> &gt; Select <code>General</code></li>
<li>Change <code>WordPress Address (URL)</code> and <code>Site Address (URL)</code> to use <code>https</code>. Click on <code>Save Changes</code>. This will throw an error message that you can disregard</li>
<li>Re-login to <code>wp-admin</code> using the new <code>https</code> URL. Go to <code>Settings</code> &gt; Select <code>Permalink</code> from menu</li>
<li>Under <code>Common Settings</code> change the URL setting to any option and revert back to your desired option</li>
</ol>
<h2 id="alternatives">Alternatives</h2>
<p>The workflow works for me because I am not afraid of databases and servers. I also do not work on Wordpress actively and may be do &lt; 5 migrations per year.</p>
<p>There are alternative solutions that are more elegant, but have dependencies on third party tools.</p>
<h4 id="plugins">Plugins</h4>
<p>There are plugins that you could use to automate the tasks in part or fully.</p>
<ol>
<li><a href="https://wordpress.org/plugins/updraftplus/">Updraft Plus</a> allows you to backup database and files from the source. You can &ldquo;restore&rdquo; the content in the destination. Updraft plus handles many complexities including updating the new host names, etc.</li>
<li><a href="https://wordpress.org/plugins/wp-migrate-db/">WP Migrate DB</a> takes care of exporting databases while allowing you to find and replace content within the database. The pro version enables you to work with files as well</li>
</ol>
<p>Despite using plugins, you still have to do the backend configuration work including -</p>
<ol>
<li>Updating <code>wp-config</code> to point to the new database and new file system (you may skip this step if you use Wordpress installation done by someone else or by one-click installers)</li>
<li><code>.htaccess</code> updates</li>
</ol>
<h4 id="cpanel-migration">CPanel Migration</h4>
<p>When you are on CPanel and changing hosts, check with your new hosting company whether they support free migrations. CPanel allows your hosting company to transfer from an existing host without any fuss - this is an one-time migration.</p>
<p>The process requires no effort from your end. Just buy new hosting, open a ticket for migration, and you are done. Your site will continue to function without problems.</p>
<p>Similar solutions may exist for other control panels too.</p>
<h2 id="you-are-done">You are done!</h2>
<p>Enjoy your new site.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Setup Ubuntu VPS - First Few Steps</title>
      <link>https://techformist.com/ubuntu-vps-setup-first-steps/</link>
      <pubDate>Thu, 11 Jun 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/ubuntu-vps-setup-first-steps/</guid>
      <description>&lt;p&gt;I create virtual private servers at least 8-12 times an year for client and personal projects. The steps to go live remain the same -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a cheapo VPS on &lt;a href=&#34;https://m.do.co/c/9f3b578e40ef&#34;&gt;Digital Ocean&lt;/a&gt;, &lt;a href=&#34;https://www.vultr.com/?ref=8605620&#34;&gt;Vultr&lt;/a&gt; (&lt;a href=&#34;https://www.vultr.com/?ref=8605622-6G&#34;&gt;get $100 credit&lt;/a&gt;), Hetzner, OVH, and friends - I end up choosing Ubuntu as the OS&lt;/li&gt;
&lt;li&gt;Secure your server&lt;/li&gt;
&lt;li&gt;Setup app server, database server and tools&lt;/li&gt;
&lt;li&gt;Stitch together various components through configuration files&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I have thought about switching to Docker (or Caprover) for months, but keep putting off any kind of automation. The adhoc sharing of DB servers, file systems, report/analytics servers make it a tricky exercise. I don&amp;rsquo;t quite spend more than 4 hours on a crazy day - so it&amp;rsquo;s not quite a big deal.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I create virtual private servers at least 8-12 times an year for client and personal projects. The steps to go live remain the same -</p>
<ol>
<li>Get a cheapo VPS on <a href="https://m.do.co/c/9f3b578e40ef">Digital Ocean</a>, <a href="https://www.vultr.com/?ref=8605620">Vultr</a> (<a href="https://www.vultr.com/?ref=8605622-6G">get $100 credit</a>), Hetzner, OVH, and friends - I end up choosing Ubuntu as the OS</li>
<li>Secure your server</li>
<li>Setup app server, database server and tools</li>
<li>Stitch together various components through configuration files</li>
</ol>
<p>I have thought about switching to Docker (or Caprover) for months, but keep putting off any kind of automation. The adhoc sharing of DB servers, file systems, report/analytics servers make it a tricky exercise. I don&rsquo;t quite spend more than 4 hours on a crazy day - so it&rsquo;s not quite a big deal.</p>
<p>Not being a sys admin myself I follow a few &ldquo;best practice&rdquo; steps recommended by smarter people on the Internet. Here&rsquo;s an easy-to-follow list for setting up a secure VPS.</p>
<h2 id="1-get-access-and-logon">1. Get Access and Logon</h2>
<p>I assume that you have -</p>
<ol>
<li>Already created the server using the browser console provided by your favourite service provider</li>
<li>You have chosen Ubuntu LTS (v20.4 at this time, though it should quite happily work the same for v18.4)</li>
<li>I use a Windows computer to connect to VPS. A few steps for configuring client will differ if you are using Linux or Mac</li>
</ol>
<p>Login to your server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh root@<span class="p">&lt;</span>password_in_email<span class="p">&gt;</span>@<span class="p">&lt;</span>server_ip&gt;
</span></span></code></pre></div><p>You will get a message like this -</p>
<pre tabindex="0"><code>The authenticity of host &#39;11.11.111.111 (11.11.111.111)&#39; can&#39;t be established.
ECDSA key fingerprint is SHA256:XXBLAH.
Are you sure you want to continue connecting (yes/no)? yes
</code></pre><p>Enter <code>yes</code> to continue.</p>
<p>You can make this step more secure when creating servers with some providers like Hetzner. You can enter the SSH public key at the time of server creation and get seamless access to the server once it is created. We will get to the public key part soon.</p>
<p>Change root password.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">passwd
</span></span></code></pre></div><p>Enter new password for root. It is a good idea to store this in LastPass or some other password management tool.</p>
<p>Add a new admin user. This id will be the default to login to your server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">adduser aweadmin
</span></span></code></pre></div><p>Enter password for new user. Next allow the new user to <code>sudo</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">usermod -aG sudo aweadmin
</span></span></code></pre></div><p>The goal is to get farther from defaults to make it more safe. This by itself will not completely secure the server, but reduce the risk of your server being of any interest to hackers.</p>
<h2 id="2-enable-automatic-security-updates">2. Enable Automatic Security Updates</h2>
<p>First, let us update the OS.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt update
</span></span><span class="line"><span class="cl">apt dist-upgrade
</span></span></code></pre></div><p>Once the update is complete, reboot server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">reboot
</span></span></code></pre></div><p>Your SSH session will disconnect and server will reboot. Re-login using SSH to your server.</p>
<p>Recent versions of Ubuntu have unattended updates by default. You can verify or change those updates -</p>
<pre tabindex="0"><code>sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
</code></pre><p>If you are using an older version, you may need to install and configure automatic updates.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt install unattended-upgrades
</span></span><span class="line"><span class="cl">nano /etc/apt/apt.conf.d/50unattended-upgrades
</span></span></code></pre></div><p>Comment out everything other than the security updates.</p>
<pre tabindex="0"><code>Unattended-Upgrade::Allowed-Origins {
//      &#34;${distro_id}:${distro_codename}&#34;;
        &#34;${distro_id}:${distro_codename}-security&#34;;
        // Extended Security Maintenance; doesn&#39;t necessarily exist for
        // every release and this system may not have it installed, but if
        // available, the policy for updates is such that unattended-upgrades
        // should also install from here by default.
        &#34;${distro_id}ESM:${distro_codename}&#34;;
//      &#34;${distro_id}:${distro_codename}-updates&#34;;
//      &#34;${distro_id}:${distro_codename}-proposed&#34;;
//      &#34;${distro_id}:${distro_codename}-backports&#34;;
};
</code></pre><p>Save file with <code>Ctrl+O</code> &gt; <code>Enter</code>. Exit the editor with <code>Ctrl+X</code>.</p>
<p>So far you have configured automatic updates. Next, enable the automatic updates.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nano /etc/apt/apt.conf.d/20auto-upgrades
</span></span></code></pre></div><p>Copy/paste the following lines -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">APT::Periodic::Update-Package-Lists <span class="s2">&#34;1&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">APT::Periodic::Download-Upgradeable-Packages <span class="s2">&#34;1&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">APT::Periodic::AutocleanInterval <span class="s2">&#34;7&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">APT::Periodic::Unattended-Upgrade <span class="s2">&#34;1&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="3-change-ssh-port-and-default-user-access">3. Change SSH Port and Default User Access</h2>
<p>We will do two things here -</p>
<ol>
<li>Change SSH port &amp; protocol</li>
<li>Enable additional SSH user and disable root access</li>
</ol>
<h3 id="change-port-and-use-protocol-2">Change Port and Use Protocol 2</h3>
<p>SSH uses port <code>22</code> by default. But, you can choose any free port between 1023 and 65535. You can find out whether a port is free with the following command -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ss -tulpn <span class="p">|</span> grep LISTEN
</span></span></code></pre></div><p>Open the config file and change the default <code>22</code> port used by SSH.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nano /etc/ssh/sshd_config
</span></span></code></pre></div><p>Find the line which begins with <code>Port</code> and change it -</p>
<pre tabindex="0"><code>Port 65432
</code></pre><p>While we are at it, let us also add a line to force SSH to always use <code>Protocol 2</code>. Just add the below line anywhere in the file.</p>
<pre tabindex="0"><code>Protocol 2
</code></pre><p><code>Ctrl+o</code> &gt; <code>Enter</code> + <code>Ctrl+x</code> to save and exit out of the editor.</p>
<p>Restart SSH.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart ssh
</span></span></code></pre></div><h3 id="enable-non-root-ssh-user">Enable non-root SSH user</h3>
<p>Open another command prompt. Try logging in to your server with the user id that was created earlier.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh -p 65432 aweadmin@11.11.111.111
</span></span></code></pre></div><p>Once you are in, try to run a <code>sudo</code> command.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ls /root
</span></span></code></pre></div><p>If things worked well so far, it is time to disable root login using SSH.</p>
<p>Open the SSH config on server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/ssh/sshd_config
</span></span></code></pre></div><p>Change the below parameter to <code>no</code> and insert a new line to enable access for the new admin user.</p>
<pre tabindex="0"><code>PermitRootLogin no
AllowUsers aweadmin
</code></pre><p><code>Ctrl+o</code> &gt; <code>Enter</code> + <code>Ctrl+x</code> to save and exit out of the editor.</p>
<p>Restart SSH.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart ssh
</span></span></code></pre></div><p>Open yet another command prompt and try logging in as <code>root</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh -p 65432 root@11.11.111.111
</span></span></code></pre></div><p>This should not work but <code>aweadmin</code> will login without problems.</p>
<h2 id="4-disable-ssh-login-with-password">4. Disable SSH login with password</h2>
<p>If you ever check the security logs on your server, you will find a series of attempts to access your server with default user ids, ports and common passwords. In this section, we will disable SSH access using password altogether and enable only clients with trusted keys to login.</p>
<p>Let us first set up the client (which is our computer = not server).</p>
<p>Run following command to generate a public/private key on your computer.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh-keygen -N <span class="s2">&#34;securepassphrase&#34;</span> -t ed25519 -C <span class="s2">&#34;mycomputer&#34;</span>
</span></span></code></pre></div><ul>
<li><code>securepassphrase</code> - can be anything that you can remember but at the same time secures access to SSH server from your computer</li>
<li><code>ed25519</code> provides the encryption algorithm used to generate keys</li>
<li><code>mycomputer</code> is just a random name which is a description for the key. You may want to provide different descriptions on other computers</li>
</ul>
<p>Accept the default file names for public and private keys (or change them). You should have two files in <code>c:\user\&lt;user_name&gt;\.ssh\</code> folder.</p>
<ul>
<li><code>id_ed25519</code> - file with private key</li>
<li><code>id_ed25519.pub</code> - public key</li>
</ul>
<p>Next, we update the generated public key on the server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh -p 65432 aweadmin@11.11.111.111
</span></span></code></pre></div><p>Create <code>.ssh</code> directory (if it doesn&rsquo;t exist) and create a new file within in.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir /home/aweadmin/.ssh
</span></span><span class="line"><span class="cl">nano /home/aweadmin/.ssh/authorized_keys
</span></span></code></pre></div><p>Copy / paste the public key from <code>id_ed25519.pub</code>.</p>
<p><code>Ctrl+o</code> &gt; <code>Enter</code> + <code>Ctrl+x</code> to save and exit out of the editor.</p>
<p>Change the default permission for the newly created file -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod <span class="m">0600</span> /home/aweadmin/.ssh/authorized_keys
</span></span></code></pre></div><p>Open a command prompt on client, and login with key.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh aweadmin@11.11.11.1111
</span></span></code></pre></div><p>You will get a different prompt that asks for the key passphrase. Enter the passphrase to gain access to the server - you are not required to type the <code>aweadmin</code>&rsquo;s password again.</p>
<p>Next, we will disable password logins in the SSH config file -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/ssh/sshd_config
</span></span></code></pre></div><p>Change following parameter -</p>
<pre tabindex="0"><code>PasswordAuthentication no
</code></pre><p><code>Ctrl+o</code> &gt; <code>Enter</code> + <code>Ctrl+x</code> to save and exit out of the editor.</p>
<p>You are required to enter passphrase when logging in each time. We can work around that.</p>
<p>Go to <code>Start</code> &gt; <code>Services</code> to list Windows services (or <code>Start</code> &gt; <code>Run</code> &gt; Enter <code>services.msc</code>). You should find a service called <code>OpenSSH Authentication Agent</code> service that will be in <code>disabled</code> state by default. Enable the service to start automatically with Windows and start the service.</p>
<p>Go to <code>Start</code> &gt; <code>Run</code> &gt; type <code>notepad c:\&lt;user&gt;\.ssh\config</code>. Enter following lines -</p>
<pre tabindex="0"><code>Host 11.11.111.111
  User aweadmin
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
  Port 65432
</code></pre><p>Save file. Ensure that the file is saved without the default <code>txt</code> extension.</p>
<p>You should now be able to login to the server without entering user id, password or key passphrase.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">ssh 11.11.111.111
</span></span></code></pre></div><p>If you use multiple computers, or want to provide access to other users, just repeat the steps to generate distinct private keys and update public key on the server.</p>
<h2 id="5-secure-your-server">5. Secure Your Server</h2>
<p>Let us install and enable software to secure the server.</p>
<h3 id="firewall">Firewall</h3>
<p>I typically use <code>UFW</code> because it is quite simple to setup and understand.</p>
<p>You can install / update <code>ufw</code> with -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install ufw
</span></span></code></pre></div><p>Check status by using -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw status
</span></span></code></pre></div><p>Configure <code>ufw</code> to allow SSH sessions on our SSH port. This is important since <code>ufw</code> closes all ports by default. Enable firewall.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw allow 65432/tcp
</span></span><span class="line"><span class="cl">sudo ufw <span class="nb">enable</span>
</span></span></code></pre></div><p>Open another command prompt and try accessing the server with default port - you should be able to login.</p>
<p>You can check the rules with the command -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw show added
</span></span></code></pre></div><h3 id="intrusion-detection">Intrusion Detection</h3>
<p>We will install <code>Fail2Ban</code> to work alongside the firewall and secure our server. <code>fail2ban</code> is an intrusion detection and prevention framework that can automatically ban IPs temporarily (or otherwise) based on invalid login attempts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo add-apt-repository universe
</span></span><span class="line"><span class="cl">sudo apt-update
</span></span><span class="line"><span class="cl">sudo apt install fail2ban
</span></span></code></pre></div><p>Copy the default config file to <code>jail.local</code> and restart service.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
</span></span><span class="line"><span class="cl">service fail2ban restart
</span></span></code></pre></div><h2 id="6-get-on-with-your-apps">6. Get on with your apps</h2>
<p>With your server setup you can start installing your applications. A few of those are super useful -</p>
<ol>
<li>
<p>Vim Editor</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install vim
</span></span></code></pre></div></li>
<li>
<p>Install <code>fish</code> as default shell, and <code>omf</code> to configure <code>fish</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get install fish
</span></span><span class="line"><span class="cl">chsh -s /usr/bin/fish
</span></span><span class="line"><span class="cl">curl -L https://get.oh-my.fish <span class="p">|</span> fish
</span></span></code></pre></div></li>
<li>
<p><a href="https://caddyserver.com/docs/install">Caddy Web server</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install caddy
</span></span></code></pre></div></li>
<li>
<p>Install <a href="https://github.com/nodesource/distributions/blob/master/README.md">Node</a> (v12.x)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sL https://deb.nodesource.com/setup_12.x <span class="p">|</span> sudo -E bash -
</span></span><span class="line"><span class="cl">sudo apt-get install -y nodejs
</span></span></code></pre></div></li>
<li>
<p><a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04">Install MySQL database</a> and configure MySQL. You will also need to open port in <code>ufw</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install mysql-server
</span></span><span class="line"><span class="cl">sudo mysql_secure_installation
</span></span></code></pre></div></li>
</ol>
<p>That&rsquo;s all folks - enjoy your brand new VPS!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A quick start on ASP.NET Core Razor Pages</title>
      <link>https://techformist.com/asp-net-razor-pages-quick-start/</link>
      <pubDate>Wed, 05 Feb 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/asp-net-razor-pages-quick-start/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a quick introduction to Razor pages in ASP.NET core, and an opinionated way to quickly start building applications using Razor pages. And yes, there is a case for using Razor pages even in 2020.&lt;/p&gt;
&lt;h3 id=&#34;so-aspnet&#34;&gt;So.. ASP.net?&lt;/h3&gt;
&lt;p&gt;Yes, indeed. I have had a love-hate relationship with ASP.NET through years. I am way less productive using ASP.net but cannot ignore the speed that a dotnet web server provides. Take into account the super debugging capabilities/tooling and the sizeable market that keeps providing projects on the platform, we surely have more than a winner in ASP.NET.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a quick introduction to Razor pages in ASP.NET core, and an opinionated way to quickly start building applications using Razor pages. And yes, there is a case for using Razor pages even in 2020.</p>
<h3 id="so-aspnet">So.. ASP.net?</h3>
<p>Yes, indeed. I have had a love-hate relationship with ASP.NET through years. I am way less productive using ASP.net but cannot ignore the speed that a dotnet web server provides. Take into account the super debugging capabilities/tooling and the sizeable market that keeps providing projects on the platform, we surely have more than a winner in ASP.NET.</p>
<p>And oh, it immensely helps that innovations like Blazor are exciting options to make web development arguably more productive and easier.</p>
<h2 id="why-yet-another-introduction">Why yet another introduction?</h2>
<p>There are a million other introductions to ASP.NET platform. Microsoft&rsquo;s own documentation and guides are immensely helpful for beginners at all stages. But, what I love to see is a quick way to create an app (or two) and make new developers comfortable with the ecosystem. At least that&rsquo;s how I learn stuff and I found some of the tutorials I glanced through to be either too detailed, or at a superficially high level.</p>
<p>This introduction is more for developers who use a different language for web development. It is not quite intended for absolute beginners, but anyone should be able to follow along.</p>
<h2 id="firing-off-with-aspnet">Firing off with ASP.net</h2>
<p>There are two main ways to get started on development using ASP.net platform.</p>
<ol>
<li>Use Visual Studio: proven way that has been forever</li>
<li>Use Visual Studio Code or your favourite editor and dotnet core CLI</li>
</ol>
<p>Both ways deserve all the love they can get. I prefer VSCode, but will use Visual Studio in this tutorial because - why not. Going forward I will be basing discussions primarily on Visual Studio 2019 and dotnet Core 3.1.</p>
<p>Installation is somewhat boring and works on expected lines.</p>
<ol>
<li>Download Visual Studio from <a href="https://visualstudio.microsoft.com/">https://visualstudio.microsoft.com/</a></li>
<li>Click, click, click to install</li>
</ol>
<p>Visual Studio comes with dotnet SDK, and you should be all set by now. If you decide to use VSCode, just install the dotnet SDK separately. You should then be able to use all the command line goodness on the best code editor ever(tm).</p>
<p>See the <a href="https://dotnet.microsoft.com/learn/dotnet/hello-world-tutorial/install">install docs</a> if you are stuck. (Stuck during install, really?)</p>
<p>Once everything&rsquo;s ready, just create a new project.</p>
<p><img loading="lazy" src="/misc/dotnet-razor-pages-new-project.jpg" type="" alt="dotnet-razor-pages-new-project"  /></p>
<p>Select &ldquo;ASP.NET Core Web Application&rdquo;. Hit next and name your project. You will get another screen where you can select an &ldquo;Web Application&rdquo; template. This option will instruct Visual Studio to scaffold a few things when the project is created.</p>
<h2 id="the-structure">The Structure</h2>
<p>As any modern web development stack, the files generated can be overwhelming at the beginning. We can break the structure down to basics.</p>
<p>A typical ASP.net project consists of one solution (which is a container for projects), and one or more projects. It will have at least one project (of course), anything else is optional and enables you to build stuff incrementally.</p>
<p><img loading="lazy" src="/misc/project-structure-visual-studio-asp-net-core-razor.jpg" type="" alt="project-structure-visual-studio-asp-net-core-razor"  /></p>
<h3 id="beginning-of-everything-startupcs-and-programcs">Beginning of everything: <code>Startup.cs</code> and <code>Program.cs</code></h3>
<p>These are the two files that bootstrap our app and give it ASP powers. When our app starts, the runtime calls <code>Main</code> method defined in <code>Program.cs</code>, which configures itself and starts running as an ASP app.</p>
<p>If you were so far wondering how our humble app came to acquire ASP powers, <code>IHostBuilder</code> is to blame. That interface provides our function <code>CreateHostBuilder</code> abilities to assume the powers of ASP.net, configure itself and start running - all in a simple one-line statement..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">CreateHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="n">Build</span><span class="p">().</span><span class="n">Run</span><span class="p">();</span>
</span></span></code></pre></div><p>More code in <code>Program.cs</code> uses <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions">C# lambdas</a> and <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1">Dependency Injection</a> (&ldquo;DI&rdquo;) to apply configuration parameters and methods defined in <code>Startup.cs</code> to our application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">IHostBuilder</span> <span class="n">CreateHostBuilder</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Host</span><span class="p">.</span><span class="n">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">ConfigureWebHostDefaults</span><span class="p">(</span><span class="n">webBuilder</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">webBuilder</span><span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span></code></pre></div><p>Further, <code>ConfigureServices</code> in <code>Startup.cs</code> has a specific block that enables Razor pages in your app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">services</span><span class="p">.</span><span class="n">AddRazorPages</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>Configure</code> method in <code>Startup.cs</code> enables a pipeline to include middleware, which in-turn enables various features for your app - all without needing you to code them in.</p>
<p>For e.g. -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span><span class="p">.</span><span class="n">UseHttpsRedirection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>&hellip; tells runtime to redirect HTTP requests to HTTPS.</p>
<p>We will deal with lambda functions, DI etc. in more detail some other time. For now, we will move on and find out how we can quickly create our app on ASP.net.</p>
<h3 id="before-we-dig-in-settings-and-launchsettingsjson">Before we dig-in: settings and <code>launchSettings.json</code></h3>
<p>We have created a web application that runs as a command line app. The server is just serving content defined in web stuff like HTML/JS/CSS etc and in Razor pages, but does not render UI on its own.</p>
<p>Configuration parameters for our app is defined in <code>launchSettings.json</code>.</p>
<p>You can see the values defined in settings file in a nice user interface - right-click on the project (not the solution) in the right-hand-side bar, and select properties. Any change in one place is reflected in the other - just sayin'.</p>
<p>You may also notice the word &ldquo;development&rdquo; in the configuration file. We can provide parameters for any number of environments including development, and the start-up logic figures out the settings to apply. Further, secrets can be managed quite efficiently and made accessible to those who &ldquo;need to know&rdquo;. This is super helpful as you might imagine.</p>
<p>By default, the app uses IISExpress but there are options to use Kestrel, which is a light-weight (&amp; a more modern) server by Microsoft. You will see these configuration options selected in the Toolbar as well. Typically, I prefer Kestrel since it is fast, and I end up hosting dotnet on Linux (where IIS is not available).</p>
<h3 id="the-client-facing-wwwroot">The &lsquo;client-facing&rsquo; <code>wwwroot</code></h3>
<p>All the client-facing assets are stored in the <code>www</code> folder. This includes CSS, JS (custom and any libraries), static HTML, and anything else that finds its way to the browser.</p>
<p>Right off the bat, you see a sample CSS and JS, bootstrap and JQuery in the scaffolded files. Any and all of this can be changed or replaced. For e.g. you can replace Bootstrap with Bulma and chug along a different path.</p>
<h3 id="pages-folder"><code>Pages</code> folder</h3>
<p>Most of the action takes place here. <code>Pages</code> is where you place razor pages created using C#. The pages in C# get converted to something that the browser can understand by ASP.NET – we will see way more of pages in a few moments. Razor pages have the extension &lsquo;cshtml&rsquo; files</p>
<p>You also see a <code>Shared</code> folder and a bunch of files starting with <code>_</code>. Shared folder is used to contain assets shared across the project, and the <code>_</code> is a convention to create something called a &lsquo;partial&rsquo;. A partial is not a &lsquo;complete&rsquo; page in itself, but comes together with other partials or pages to form complete pages.</p>
<p><code>Layout.cshtml</code> is an example of a partial. In this file we have the header (which forms the header in the pages using this layout), a container (which will contain data), and the footer. The file also has references to script and HTML.</p>
<p>Of course, you can use different layouts, or decide not to use layouts at all – but building pages like this will make the entire process easy to build and to maintain.</p>
<p>In other notes, take a look at -</p>
<ul>
<li><code>_ViewImports.cshtml</code>: enables tag helpers globally</li>
<li><code>_ViewStart.cshtml</code>: base/master page</li>
</ul>
<p>Error, Index and Privacy pages are the actual Razor pages. You will create something on these lines to for adding more pages to your app.</p>
<p>Expand <code>Index.cshtml</code> page to find a file called <code>Index.cshtml.cs</code>. This file has the C# code to go along with the mark-up page. You can refer to the variables, methods, etc. from this c# page in the mark-up <code>cshtml</code> page. This arrangement is akin to mark-up + code arrangement in any Vue/React/Svelte application. In Blazor, you can have mark-up + code in same or different files - but that is for a different day.</p>
<p>This structure is different from a typical MVC model that touts a distinct controller, model and view layer. These functions are present in the Razor page app, but not fashioned in MVC style.</p>
<h2 id="more-about-the-internals-and-workings-of-an-aspnet-app">More about the internals and workings of an ASP.net app</h2>
<p>We are now masters of the structure of ASP.net core razor page project, and it is time to move on and see how everything comes together.</p>
<p>If you&rsquo;re serious about your trade, you will skip this section altogether. The terms here just help you speak the same language - you will probably figure this out by yourself while building the actual app.</p>
<h3 id="routing">Routing</h3>
<p>The <code>Pages</code> folder takes care of routing in our application. The files in the folder represent the URLs served by your application.</p>
<p>Let&rsquo;s see how..</p>
<ul>
<li><code>Privacy.cshtml</code> is a file in the <code>Pages</code> folder</li>
<li><code>baseurl.com/privacy</code> will request the page named &lsquo;privacy.cshtml&rsquo; in the folder. ASP.net serves the HTML generated from the razor page.</li>
<li>Any sub-folder will also get included in the path. For e.g. a <code>blog/Hello.cshtml</code> is rendered on <code>baseurl.com/blog/Hello</code></li>
<li>If the requested page does not exist, a &lsquo;page cannot be found&rsquo; error is displayed by default. You can configure the application to redirect requests to any other page (e.g. <code>index</code>) if the page cannot be located</li>
</ul>
<p>You can override the default routing using the <code>@Page</code> tag.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">@page</span> <span class="s">&#34;/privacy1&#34;</span>
</span></span></code></pre></div><p>.. will enable the link to <code>baseurl.com/privacy1</code> and the previously used <code>/privacy</code> page disappears into oblivion.</p>
<h2 id="start-tinkering">Start Tinkering</h2>
<p>Run your app by hitting <code>Ctrl + F5</code>. Visual Studio opens the browser (or allows you to configure browser) and serves the application. You can see the starter page and navigate through the links provided in the scaffolded application.</p>
<p>You will see that any changes will require you to build/restart app. We can avoid that by executing our app in watch mode..</p>
<pre tabindex="0"><code>cd &lt;project-folder&gt;
dotnet run watch
</code></pre>]]></content:encoded>
    </item>
    
    <item>
      <title>Free Excel Template for Scrum Projects</title>
      <link>https://techformist.com/free-agile-scrum-excel-template/</link>
      <pubDate>Wed, 29 Jan 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/free-agile-scrum-excel-template/</guid>
      <description>&lt;p&gt;Excel is the only program you need to survive in IT. So, how about a template for tracking tasks in an Agile Scrum project?&lt;/p&gt;
&lt;h2 id=&#34;why-scrum&#34;&gt;Why Scrum?&lt;/h2&gt;
&lt;p&gt;If you are looking for the answer on why agile / scrum -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You are in the wrong site&lt;/li&gt;
&lt;li&gt;That is well beyond the scope of this post&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In short: Scrum is a framework to run your project using Agile methodologies. Scrum&amp;rsquo;s objectives are -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Self-organise&lt;/li&gt;
&lt;li&gt;Learn by doing things and through experience&lt;/li&gt;
&lt;li&gt;Continuously improve&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Read more on &lt;a href=&#34;https://www.atlassian.com/agile/scrum&#34;&gt;Atlassian&amp;rsquo;s site.&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Excel is the only program you need to survive in IT. So, how about a template for tracking tasks in an Agile Scrum project?</p>
<h2 id="why-scrum">Why Scrum?</h2>
<p>If you are looking for the answer on why agile / scrum -</p>
<ol>
<li>You are in the wrong site</li>
<li>That is well beyond the scope of this post</li>
</ol>
<p>In short: Scrum is a framework to run your project using Agile methodologies. Scrum&rsquo;s objectives are -</p>
<ol>
<li>Self-organise</li>
<li>Learn by doing things and through experience</li>
<li>Continuously improve</li>
</ol>
<p>Read more on <a href="https://www.atlassian.com/agile/scrum">Atlassian&rsquo;s site.</a></p>
<p>The stated objectives for Scrum need a simple to use, centralised store - which an Excel document is generally not. But, I don&rsquo;t let all that come in the way to create a beautiful template.</p>
<h2 id="download-excel-template">Download Excel Template</h2>
<p>If you like to live on the edge and download from any website -</p>
<p><a href="/misc/scrum_project_template.xlsx">Download free Scrum Excel-template</a>.</p>
<p>If you are not that naive, you can check out what this template can do (or even copy the workbook) on Google Sheets.</p>
<p>See the action on <a href="https://docs.google.com/spreadsheets/d/1RzQTkJvsElzVP1aHj49nfHdmFiedEzAnauqm1dqHCZ8/edit?usp=sharing">Google Docs</a>.</p>
<p>The template is -</p>
<ul>
<li>free to use for any kind of project</li>
<li>released to public domain - see the sole exception in the terms below</li>
</ul>
<p>You can stop reading now, and get back to work in 99.99% of the cases.</p>
<h2 id="why-use-excel-to-track-scrum-and-why-this-template">Why use Excel to track Scrum and why this template?</h2>
<ul>
<li>There are smaller teams that rely on part-time developers who don&rsquo;t want overhead</li>
<li>Small teams may discuss on &ldquo;stuff&rdquo; and move on with work, and don&rsquo;t particularly care about documenting everything</li>
<li>Simple projects may not have the budget to organise things</li>
</ul>
<p>And yes, I know that these are excuses for not creating a &ldquo;usable&rdquo; tool. But organisations continue to live in a bubble.</p>
<p>So, here we are.</p>
<p>Using this template you can -</p>
<ol>
<li>track projects and sprints</li>
<li>track backlog, impediments and retrospective remarks</li>
<li>record effort against tasks (which can relate back to backlog)</li>
<li>summarise effort and show beautiful charts</li>
</ol>
<p>There are no fancy formulae, macros, and automation to take the dog on a walk when you work.</p>
<p>All that said, I think no one should use Excel templates for use cases like this. But I really can&rsquo;t quite preach something else when I have been an ardent fan of their simplicity.</p>
<h2 id="terms-of-use">Terms of Use</h2>
<p>No terms, really. You can do what you want with this template.</p>
<p>But, I would appreciate if you could donate a dollar or more to the nearest charity, or to a person in need.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A story of company case studies and API testing</title>
      <link>https://techformist.com/2020/01/15/a-story-of-company-case-studies-and-api-testing/</link>
      <pubDate>Wed, 15 Jan 2020 15:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/2020/01/15/a-story-of-company-case-studies-and-api-testing/</guid>
      <description>&lt;p&gt;#rant that none asked for.&lt;/p&gt;
&lt;p&gt;I came across this whitepaper on testing micro services. I clicked on &lt;a href=&#34;https://www.infosys.com/services/validation-solutions/case-studies/microservices-testing.html&#34;&gt;this link&lt;/a&gt; because I am writing microservices now but I am fairly new to microservices.&lt;/p&gt;
&lt;p&gt;I did a mistake that I have repeated 1593 times in the past. The said link was a resource by one of the big IT service providers.&lt;/p&gt;
&lt;p&gt;While the work indeed is commendable - automating 4000 test cases is no joke, the content and approach for highlighting the &amp;ldquo;best practice&amp;rdquo; leave much to be desired.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>#rant that none asked for.</p>
<p>I came across this whitepaper on testing micro services. I clicked on <a href="https://www.infosys.com/services/validation-solutions/case-studies/microservices-testing.html">this link</a> because I am writing microservices now but I am fairly new to microservices.</p>
<p>I did a mistake that I have repeated 1593 times in the past. The said link was a resource by one of the big IT service providers.</p>
<p>While the work indeed is commendable - automating 4000 test cases is no joke, the content and approach for highlighting the &ldquo;best practice&rdquo; leave much to be desired.</p>
<p>The paper highlights practices that have been assumed to be best practices for a long time - and provides absolutely no information other than to say &ldquo;we did so and so&rdquo;. &ldquo;Extreme automation&rdquo; is the buzzword of the day, and as we, of the corporate types, agree - it doesn&rsquo;t get better than that.</p>
<p>I mean - yes, I understand this is a mere case study. I don&rsquo;t expect every case study or paper to contain all the real &ldquo;stuff&rdquo; all the time, but following would have been welcome -</p>
<ol>
<li>How does the accelerator fare as compared to commercial tools today?</li>
</ol>
<ul>
<li>Savings in license costs by avoiding commercial tools (typically clients don&rsquo;t pay or think they don&rsquo;t pay for &ldquo;accelerators&rdquo;)</li>
<li>How well did the accelerator write tests? What was the coverage advantage vis-a-vis other tools</li>
<li>How did developers breathe easy by using this tool as compared to say writing tests in Postman (Pro), or using other open source and free tools that are popular today</li>
</ul>
<ol>
<li>By how much did the framework improve efficiency in generating data?</li>
<li>How was a high degree of overlap achieved in smart sharing of regression/system test cases, while keeping effort and test duration minimal</li>
</ol>
<p>Take note that I have still not outlined just how I am doing the &ldquo;thing&rdquo;. That may be part of the IP, or reflect my &ldquo;scarcity mindset&rdquo;. But, readers only get excited about the work when they know more about the advantages of tools and practices employed in the project.</p>
<p>A more complete paper would probably also cover -</p>
<ol>
<li>Employing live vs. test data, and how the framework can help in specific cases. How we found advantage in one or both?</li>
<li>How performance tests play a greater role today? How can they be integrated in the overall cycle? How are they automated using existing toolsets?</li>
<li>How well can we mock or virtualise services more efficiently given a specific set of constraints?</li>
</ol>
<h1 id="its-not-all-bad">It&rsquo;s not all bad</h1>
<p>Don&rsquo;t get me wrong. I can understand where the above content is coming from. Marketing pressures, typical development processes not covering best practices, and lack of resources to think about content strategy through and through - they are all real.</p>
<p>And.. I have been there multiple times. I have been guilty of providing such content, case studies and &ldquo;white papers&rdquo; in the past (and probably churn that out again?) - content that lacks substance for so called &ldquo;expert readers&rdquo; but content nevertheless. I hope the servers hosting them will burn to ground so that I can lecture on best practices of writing good content. Oh wait, I hope it does not include this website too.</p>
<p>Fortunately, good content is everywhere today. Want to automate your API testing and write something about it? I would say start from these posts -</p>
<ul>
<li>know what API testing is about - <a href="https://martinfowler.com/articles/microservice-testing/#agenda">https://martinfowler.com/articles/microservice-testing/#agenda</a></li>
<li>free and open source tools - <a href="https://swagger.io/tools/open-source/open-source-integrations/">Integrations of swagger with open source tools</a> including <a href="https://github.com/Maks3w/SwaggerAssertions">SwaggerAssertions</a>, <a href="https://www.npmjs.com/package/swagger-test">swagger-test</a></li>
<li>how a popular tool aids writing your tests - look up Postman Pro</li>
<li>how a seemingly successful product helps test automation - <a href="https://assertible.com/blog/testing-an-api-using-swagger">https://assertible.com/blog/testing-an-api-using-swagger</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Automatic Dynamic Sidebars in Vuepress</title>
      <link>https://techformist.com/automatic-dynamic-sidebar-vuepress/</link>
      <pubDate>Wed, 08 Jan 2020 15:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/automatic-dynamic-sidebar-vuepress/</guid>
      <description>&lt;p&gt;How can you get automatic sidebars to be generated in Vuepress depending on the page?&lt;/p&gt;
&lt;h2 id=&#34;the-situation&#34;&gt;The Situation&lt;/h2&gt;
&lt;p&gt;Vuepress is simple.&lt;/p&gt;
&lt;p&gt;Being simple is rather difficult. In the case of Vuepress, the difficulty can show up in unexpected places.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://techformist.com/using-vuepress/&#34;&gt;Starting with Vuepress is simple enough&lt;/a&gt;. Equally simple is enabling navigation on your site at multiple levels through a &amp;ldquo;sidebar&amp;rdquo; and/or a &amp;ldquo;navbar&amp;rdquo;. Specifically for the sidebar - you can enable in by adding a couple of lines in &lt;code&gt;/docs/.vuepress/config.js&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>How can you get automatic sidebars to be generated in Vuepress depending on the page?</p>
<h2 id="the-situation">The Situation</h2>
<p>Vuepress is simple.</p>
<p>Being simple is rather difficult. In the case of Vuepress, the difficulty can show up in unexpected places.</p>
<p><a href="/using-vuepress/">Starting with Vuepress is simple enough</a>. Equally simple is enabling navigation on your site at multiple levels through a &ldquo;sidebar&rdquo; and/or a &ldquo;navbar&rdquo;. Specifically for the sidebar - you can enable in by adding a couple of lines in <code>/docs/.vuepress/config.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//...
</span></span></span><span class="line"><span class="cl">  <span class="nx">themeConfig</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sidebar</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Home&#34;</span><span class="p">,</span> <span class="nx">children</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">]</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Misc&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">children</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;more&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//...
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>The above change will create a sidebar for your site with links under <code>Home</code> and <code>Misc</code>. The title of links will be either -</p>
<ul>
<li>the title of the page (or)</li>
<li>the first header element within the markdown files that the links point to</li>
</ul>
<p>This is how it looks -</p>
<p><img loading="lazy" src="/misc/vuepress-site-navbar-sidebar.gif" type="" alt="vuepress-site-navbar-sidebar"  /></p>
<p>As you can see: the <code>H2</code> headers will be links grouped below the file links.</p>
<p>But, what if you need to do more -</p>
<ol>
<li>you want to create different sidebars for different pages. For e.g. if you are creating a site that has a bunch of different guides instead of one guide with multiple chapters. When you go to specific pages (which are represented as folders in Vuepress root folder), the navigation links have to change to point to different markdown files in a specific folder</li>
<li>you may want to continue having <code>H2</code> behaviour be Vuepress default - show the links within the specific markdown files</li>
<li>you <em>don&rsquo;t want</em> to write links to all the files by hand. Instead you want the system to automatically &ldquo;see&rdquo; the content to be linked</li>
</ol>
<p>In a recent project I needed the &ldquo;Admin&rdquo;, &ldquo;Developer&rdquo; and other folders to have their own sidebars with navigation links. -</p>
<p><img loading="lazy" src="/misc/vuepress-dynamic-sidebar-appearance.jpg" type="" alt="vuepress-dynamic-sidebar-appearance"  /></p>
<p>This sidebar is slightly different from Vuepress default, which seemingly assumes that all of the content is one big happy family. Vuepress by default enables links just point to the different markdown files in the document root, and to the <code>H2</code> elements when the parent links are expanded.</p>
<h2 id="solutions">Solutions</h2>
<p>Let&rsquo;s look at how we can customise sidebar in Vuepress.</p>
<p>Vuepress is after all powered by Vue - so customisation is not really a complex problem. However, I did not find this as straight forward as something like <a href="https://gridsome.org/">Gridsome</a>. In Gridsome you just create a component for navigation like any sane developer and the system will exactly do what it has to - render the navbar.</p>
<p>Vuepress&rsquo;s navbar is driven by config (and if applicable, plugins) - so this seemed not so elegant(!?) at first. But as always - all you need to solve world&rsquo;s problems are good docs and some time.</p>
<p>Given below are three solutions that I tried to different degrees of success. The solution that worked for me is at the end because I am heartless and believe in making people scroll.</p>
<h3 id="solution-1-use-vuepress-plugin-auto-sidebar-plugin">Solution 1. Use <code>vuepress-plugin-auto-sidebar</code> plugin</h3>
<p>The plugin <a href="https://github.com/shanyuhai123/vuepress-plugin-auto-sidebar">vuepress-plugin-auto-sidebar</a> seemed to do exactly what I wanted.</p>
<p>To install a plugin in Vuepress, all you have to do is -</p>
<ol>
<li>Install the package using npm/yarn</li>
<li>Use the plugin in config</li>
</ol>
<p>Let&rsquo;s get this going. Go to Vuepress folder and install package.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm install --save vuepress-plugin-auto-sidebar
</span></span></code></pre></div><p>Include plugin in <code>/docs/.vuepress/config.js</code>-</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s2">&#34;vuepress-plugin-auto-sidebar&#34;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">titleMap</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;salesforce-for-students&#34;</span><span class="o">:</span> <span class="s2">&#34;Salesforce for Students&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;pd1-guide&#34;</span><span class="o">:</span> <span class="s2">&#34;Platform Developer I Certification Guide&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;admin-guide&#34;</span><span class="o">:</span> <span class="s2">&#34;Admin Certification Guide&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;why-learn-salesforce&#34;</span><span class="o">:</span> <span class="s2">&#34;Why Learn Salesforce?&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">misc</span><span class="o">:</span> <span class="s2">&#34;Miscellany&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This configuration will provide custom titles for the different folders and automatically pick up the files within the folder to create distinct, dynamic navigation bars.</p>
<p>In the ideal world, this should have been it and I should not have been sitting around to create a blog post about all the circus. But hey - ideal world is not exciting.</p>
<ol>
<li>Getting started was bit of a bother. The documentation of the package is in Chinese and there are not many examples. <a href="https://translate.googleusercontent.com/translate_c?depth=1&amp;hl=en&amp;prev=search&amp;rurl=translate.google.com&amp;sl=zh-CN&amp;sp=nmt4&amp;u=https://github.com/shanyuhai123/vuepress-plugin-auto-sidebar&amp;usg=ALkJrhjEoGnpFTMIIBtnn7mQe2ZOOMElQw">Google translation helped to an extent</a></li>
<li>I could not quite figure out how <code>autoNext</code> and <code>autoPrev</code> worked. My files were not exactly sorted in the folder and sort order of the links in the sidebar was messed up</li>
<li>There were random failures. You could never say when sidebars generation failed (silently), and for the life of me, I could not figure out why. With dozens of files in different folders, I was quite frustrated with all the cut/pasting of content just to see what caused the program to fail</li>
<li>If you have a sub-folder with a <code>README</code> in the root, the README does not show in the navigation. This is expected as per Vuepress, but I could never say when the README appeared as a separate link and when it wouldn&rsquo;t</li>
</ol>
<h3 id="solution-2-use-vuepress-bar">Solution 2: Use <code>vuepress-bar</code></h3>
<p><a href="https://www.npmjs.com/package/vuepress-bar">vuepress-bar</a> was another ready solution that seemed promising.</p>
<p>First, install the package.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vuepress-bar
</span></span></code></pre></div><p>Next, include the plugin in <code>/docs/.vuepress/config.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">getConfig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;vuepress-bar&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">themeConfig</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="nx">getConfig</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">__dirName</span><span class="si">}</span><span class="sb">/..`</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>That&rsquo;s it - you should have the new sidebars working. This worked more reliably than solution (1), but I could not figure out how to generate distinct sidebars for indidvidual content folders. All links appeared under one title - which was not ideal for my case.</p>
<p><img loading="lazy" src="/misc/vuepress-bar-sidebar.jpg" type="" alt="vuepress-bar-sidebar"  /></p>
<p>Consider using this library if you just want a dynamic sidebar (and/or a navbar).</p>
<h3 id="solution-3-go-custom">Solution 3: Go custom</h3>
<p>My final solution was just doing everything myself. This was not as scary as it initially appeared - all it took was a couple of lines of code.</p>
<p>Get started by doing changes in <code>/docs/.vuepress/config.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;fs&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;path&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//...
</span></span></span><span class="line"><span class="cl">  <span class="nx">themeConfig</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sidebar</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;/why-learn-salesforce/&#34;</span><span class="o">:</span> <span class="nx">getSideBar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;why-learn-salesforce&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Why Salesforce?&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;/admin-guide/&#34;</span><span class="o">:</span> <span class="nx">getSideBar</span><span class="p">(</span><span class="s2">&#34;admin-guide&#34;</span><span class="p">,</span> <span class="s2">&#34;Admin Certification Guide&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;/pd1-guide/&#34;</span><span class="o">:</span> <span class="nx">getSideBar</span><span class="p">(</span><span class="s2">&#34;pd1-guide&#34;</span><span class="p">,</span> <span class="s2">&#34;PD1 Certification Guide&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;/salesforce-for-students/&#34;</span><span class="o">:</span> <span class="nx">getSideBar</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;salesforce-for-students&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Salesforce for Students&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//...
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getSideBar</span><span class="p">(</span><span class="nx">folder</span><span class="p">,</span> <span class="nx">title</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">extension</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;.md&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">fs</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">readdirSync</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">__dirname</span><span class="si">}</span><span class="sb">/../</span><span class="si">${</span><span class="nx">folder</span><span class="si">}</span><span class="sb">`</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">filter</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">item</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&#34;readme.md&#34;</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fs</span><span class="p">.</span><span class="nx">statSync</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">__dirname</span><span class="si">}</span><span class="sb">/../</span><span class="si">${</span><span class="nx">folder</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="nx">item</span><span class="p">)).</span><span class="nx">isFile</span><span class="p">()</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">extension</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">extname</span><span class="p">(</span><span class="nx">item</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">[{</span> <span class="nx">title</span><span class="o">:</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">children</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="p">...</span><span class="nx">files</span><span class="p">]</span> <span class="p">}];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And voila, you have the brand new sidebar -</p>
<p><img loading="lazy" src="/misc/vuepress-sidebar-auto-generate-function.jpg" type="" alt="vuepress-sidebar-auto-generate-function"  /></p>
<p>You can see that -</p>
<ol>
<li>There&rsquo;s a distinct sidebar for each folder - first level links are the files within the folder</li>
<li>Links to <code>README</code> and other files are consistent</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Using Vuepress and other stories</title>
      <link>https://techformist.com/using-vuepress/</link>
      <pubDate>Wed, 04 Dec 2019 18:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/using-vuepress/</guid>
      <description>&lt;p&gt;I am a big fan of Vue and cannot breathe without static sites. So, it is only natural that I play around and implement Vue-based static sites for fun and profit.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a distilled-down version of my experience with &lt;a href=&#34;https://vuepress.vuejs.org/&#34;&gt;Vuepress&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-situation&#34;&gt;The Situation&lt;/h2&gt;
&lt;p&gt;I create static sites that are &amp;ldquo;real&amp;rdquo; websites. Typically the following factors in a static site generator help -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A cool, easy-to-customise home page&lt;/li&gt;
&lt;li&gt;Themes/starter projects that make life easy&lt;/li&gt;
&lt;li&gt;Plugins that can extend functionality of core software&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Although I had played around Vuepress quite a bit, I always ended up choosing non-Vue technologies like Hugo, or Vue-based static site generators like Gridsome or Saber.land.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I am a big fan of Vue and cannot breathe without static sites. So, it is only natural that I play around and implement Vue-based static sites for fun and profit.</p>
<p>Here&rsquo;s a distilled-down version of my experience with <a href="https://vuepress.vuejs.org/">Vuepress</a>.</p>
<h2 id="the-situation">The Situation</h2>
<p>I create static sites that are &ldquo;real&rdquo; websites. Typically the following factors in a static site generator help -</p>
<ol>
<li>A cool, easy-to-customise home page</li>
<li>Themes/starter projects that make life easy</li>
<li>Plugins that can extend functionality of core software</li>
</ol>
<p>Although I had played around Vuepress quite a bit, I always ended up choosing non-Vue technologies like Hugo, or Vue-based static site generators like Gridsome or Saber.land.</p>
<p>More recently however, I decided to have this long drawn out project to create a free tutorial website. With great delight and big plans, I chose Vuepress.</p>
<p>This is that true story.</p>
<h2 id="what-exactly-is-vuepress">What exactly is Vuepress?</h2>
<p>A Vue-based static site generator, which is created by Evan Yu and supported by the Vue team. If that is not enough to get you going, there are other factors too. Read on.</p>
<p>Vuepress was created for documentation sites but has official themes/plugins to create a blog as well.</p>
<ul>
<li>Create documentation sites</li>
<li>Create full-scale websites (we will get back to this point)</li>
<li>Create blogs</li>
<li>Create eCommerce sites</li>
<li>Make sites interactive with the power of Vue</li>
</ul>
<p>Vuepress is simple, provides a standard experience, (kind of) easy to configure and easy to get started.</p>
<h2 id="getting-started-on-vuepress">Getting Started on Vuepress</h2>
<p>Create a project folder and install Vuepress.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">mkdir</span> awedocs
</span></span><span class="line"><span class="cl"><span class="k">cd</span> awedocs
</span></span><span class="line"><span class="cl">yarn init
</span></span><span class="line"><span class="cl">yarn add -D vuepress
</span></span></code></pre></div><p>And then.. be prepared to act surprised - because there is nothing really there. There is little to see fresh out of the box. We need a few more steps to get there.</p>
<p>Open the <code>awedocs</code> folder in your VSCode. Create a sub directory called <code>docs</code> in the root folder. Then, create a file named <code>README.md</code> in <code>docs</code> folder. Enter following content in the file..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">Hello world
</span></span></code></pre></div><p><code>md</code> or markdown files will contain the content of your site. This file should be good for initial tests.</p>
<p>Open <code>package.json</code> and enter scripts to run Vuepress. Your entire file may look like below -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;awedocs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Awesome docs.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;vuepress dev docs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;vuepress build docs&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;main&#34;</span><span class="p">:</span> <span class="s2">&#34;index.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;repository&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/techformist/awedocs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;author&#34;</span><span class="p">:</span> <span class="s2">&#34;Prashanth Krishnamurthy&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;license&#34;</span><span class="p">:</span> <span class="s2">&#34;MIT&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;devDependencies&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;vuepress&#34;</span><span class="p">:</span> <span class="s2">&#34;^1.2&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;dependencies&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Go back to command prompt, and issue the command ..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">yarn dev
</span></span></code></pre></div><p><img loading="lazy" src="/misc/vuepress-dev-start.png" type="" alt="vuepress-dev-start"  /></p>
<p>.. and ta da - you have your Vuepress site up and running.</p>
<p><img loading="lazy" src="/misc/default-site-vuepress.png" type="" alt="default-site-vuepress"  /></p>
<p>You can make a few smart observations -</p>
<ol>
<li>Vuepress is minimal (at least in the beginning)</li>
<li>Vuepress comes with a default theme</li>
<li>Content and presentation layer is well isolated with the presentation layer</li>
</ol>
<p>This means that -</p>
<ol>
<li>you can get started really really quickly. Install Vuepress -&gt; start creating documents / blog posts</li>
<li>you don&rsquo;t need to learn Vue/Vuepress to use Vuepress to create a documentation site or blog</li>
</ol>
<h2 id="making-vuepress-your-own">Making Vuepress your own</h2>
<p>The site generated so far is functional but too minimal. Let&rsquo;s add some jazz.</p>
<p>Update <code>README.md</code> with following content -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">home: true
</span></span><span class="line"><span class="cl">heroImage: /images/dog.jpg
</span></span><span class="line"><span class="cl">heroText: Awedocs
</span></span><span class="line"><span class="cl">tagline: docs. docs. docs.
</span></span><span class="line"><span class="cl">actionText: Get Started →
</span></span><span class="line"><span class="cl">actionLink: /more
</span></span><span class="line"><span class="cl">features:
</span></span><span class="line"><span class="cl">  <span class="k">-</span> title: Good
</span></span><span class="line"><span class="cl">    details: Know everything.
</span></span><span class="line"><span class="cl">  <span class="k">-</span> title: Better
</span></span><span class="line"><span class="cl">    details: Know something.
</span></span><span class="line"><span class="cl">  <span class="k">-</span> title: Best
</span></span><span class="line"><span class="cl">    details: Know nothing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">footer: MIT Licensed
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Hello world!
</span></span></span></code></pre></div><p>The content between <code>---</code> is called frontmatter. This tells Vuepress to mete out some special treatment to the file.</p>
<p>Now do two things to support above content -</p>
<ol>
<li>
<p>Add a file called <code>more.md</code> alongside <code>README.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gh"># Morrreee
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is much more information than I can handle.
</span></span></code></pre></div></li>
<li>
<p>Create a new folder <code>/docs/.vuepress/images</code>. Save a sample image and name it <code>dog.jpg</code>.</p>
</li>
</ol>
<p>Stop Vuepress dev server. Start it again.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">yarn dev
</span></span></code></pre></div><p>You have something that resembles a typical website.</p>
<p><img loading="lazy" src="/misc/start-page-with-frontmatter-vuepress.png" type="" alt="start-page-with-frontmatter-vuepress"  /></p>
<p>The <code>/docs/.vuepress</code> folder will have any and all customisations for your Vuepress instance. This includes -</p>
<ul>
<li>Vue Components</li>
<li>Navigation bars</li>
<li>Additional Javascript</li>
<li>Styles</li>
</ul>
<p>Content stays in the <code>/docs</code> folder.</p>
<h2 id="add-more-ui-elements">Add more UI elements</h2>
<p>Lets add a sidebar and navbar.</p>
<p>Create a file called <code>/docs/.vuepress/config.js</code>, which will house all the configuration for Vuepress. Introduce following content in the file -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;awedocs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">description</span><span class="o">:</span> <span class="s2">&#34;Awesome docs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">themeConfig</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;English&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">editLinkText</span><span class="o">:</span> <span class="s2">&#34;Edit this page on GitHub&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">lastUpdated</span><span class="o">:</span> <span class="s2">&#34;Last Updated&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">nav</span><span class="o">:</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;./nav&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sidebar</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Home&#34;</span><span class="p">,</span> <span class="nx">children</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">]</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Misc&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">children</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;more&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">repo</span><span class="o">:</span> <span class="s2">&#34;techformist/awedocs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">editLinks</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@vuepress/back-to-top&#34;</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Create a file called <code>.vuepress/nav.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Let Me Google&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">link</span><span class="o">:</span> <span class="s2">&#34;https://google.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;About&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">items</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;About&#34;</span><span class="p">,</span> <span class="nx">link</span><span class="o">:</span> <span class="s2">&#34;/about&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Terms&#34;</span><span class="p">,</span> <span class="nx">link</span><span class="o">:</span> <span class="s2">&#34;/terms&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span></code></pre></div><p>Restart Vuepress to see this -</p>
<p><img loading="lazy" src="/misc/vuepress-site-navbar-sidebar.gif" type="" alt="vuepress-site-navbar-sidebar"  /></p>
<h2 id="build-and-deploy">Build and Deploy</h2>
<p>Once you are ready, you do a simple -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">yarn build
</span></span></code></pre></div><p>This will create a <code>docs/.vuepress/dist</code> folder. Upload this folder to your host and you are done!</p>
<h2 id="what-could-i-do-next">What could I do next?</h2>
<p>Well, sky&rsquo;s the limit. Vuepress is based off Vue and Javascript - so you can do nothing short of magic.</p>
<p>There are a few things that can be highlighted here -</p>
<h3 id="1-add-simple-vue-components-and-interactivity">1. Add simple Vue components and interactivity</h3>
<p>We can include interactive components in markdown.</p>
<p>Create a Vue component called <code>AddNum.vue</code> in <code>docs/.vuepress/components/</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;a&#34;</span><span class="p">&gt;</span>Num 1<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;a&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;a&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;number&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;b&#34;</span><span class="p">&gt;</span>Num 2<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;a&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;number&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;b&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;right&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold; margin-top:15px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;sum&#34;</span><span class="p">&gt;</span>Sum =<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;sum&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;number&#34;</span> <span class="na">:value</span><span class="o">=</span><span class="s">&#34;sum&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;right&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">a</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">b</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">sum</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">input</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Include this component in <code>more.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gh"># Morrreee
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is much more information than I can handle.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">But, I could do a sum for you.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;AddNum/&gt;
</span></span></code></pre></div><p>See the magic of adding numbers on the fly.</p>
<h3 id="create-even-more-complex-components">Create even more complex components</h3>
<p>Let us have a Vue gallery for our glorious site.</p>
<p>In the root folder do an install.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">yarn add vue-gallery
</span></span></code></pre></div><p>Create <code>/docs/.vuepress/Galleria.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- code from https://www.npmjs.com/package/vue-gallery --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;padme&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">gallery</span> <span class="na">:images</span><span class="o">=</span><span class="s">&#34;images&#34;</span> <span class="na">:index</span><span class="o">=</span><span class="s">&#34;index&#34;</span> <span class="err">@</span><span class="na">close</span><span class="o">=</span><span class="s">&#34;index = null&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">gallery</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;image&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(image, imageIndex) in images&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:key</span><span class="o">=</span><span class="s">&#34;imageIndex&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;index = imageIndex&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:style</span><span class="o">=</span><span class="s">&#34;{
</span></span></span><span class="line"><span class="cl"><span class="s">        backgroundImage: &#39;url(&#39; + image + &#39;)&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        width: &#39;300px&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        height: &#39;200px&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">      }&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">VueGallery</span> <span class="nx">from</span> <span class="s2">&#34;vue-gallery&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">images</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;https://dummyimage.com/800/ffffff/000000&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;https://dummyimage.com/1600/ffffff/000000&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;https://dummyimage.com/1280/000000/ffffff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;https://dummyimage.com/400/000000/ffffff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nx">index</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">gallery</span><span class="o">:</span> <span class="nx">VueGallery</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">image</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">float</span><span class="p">:</span> <span class="k">clear</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-size</span><span class="p">:</span> <span class="kc">cover</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-repeat</span><span class="p">:</span> <span class="kc">no-repeat</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-position</span><span class="p">:</span> <span class="kc">center</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#ebebeb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">padme</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-top</span><span class="p">:</span> <span class="mf">2.5</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-bottom</span><span class="p">:</span> <span class="mf">2.5</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Include this component in our <code>more.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gh"># Morrreee
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is much more information than I can handle.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Sum
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">But, I could do a sum for you.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;AddNum/&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Gallery
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Or, I could show a gallery. Click on the image to view full screen.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Galleria/&gt;
</span></span></code></pre></div><p>You have a fully functioning gallery!</p>
<p>Note that we did not add this component anywhere other than creating a <code>vue</code> file, nor did we <code>use</code> the component.</p>
<h3 id="3-use-a-vue-plugin">3. Use a Vue plugin</h3>
<p>Since Vuepress has Vue behind the scenes, we could hop on to the same magic train for our own nefarious schemes.</p>
<p>Do yet another install.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">yarn add vue-typed-js
</span></span></code></pre></div><p>Create a new Vue file - <code>/docs/.vuepress/components/</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">vue-typed-js</span>
</span></span><span class="line"><span class="cl">      <span class="na">:strings</span><span class="o">=</span><span class="s">&#34;[
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;once&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;twice&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;thrice&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;four times&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;five times&#39;,
</span></span></span><span class="line"><span class="cl"><span class="s">        &#39;infinity&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">      ]&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:loop</span><span class="o">=</span><span class="s">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h4</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color:red&#34;</span><span class="p">&gt;</span>Going.. <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;typing&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;&lt;/</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">vue-typed-js</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Again, add this new component to our <code>more.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gh"># Morrreee
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is much more information than I can handle.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Sum
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">But, I could do a sum for you.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;AddNum/&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Gallery
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Or, I could show a gallery. Click on the image to view full screen.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Galleria/&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Typing Ahoy
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I could even show this cool type simulator.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;TypeAhoy/&gt;
</span></span></code></pre></div><p>You now have some typing magic :)</p>
<p><img loading="lazy" src="/misc/vuepress-interactivity-vue-components.gif" type="" alt="vuepress-interactivity-vue-components"  /></p>
<h2 id="custom-themes-and-plugins">Custom themes and plugins</h2>
<p>So far you have seen themes provided by default in Vuepress. You can extend the theme or use a theme developed by third parties.</p>
<p>Similarly, you can develop custom functionality using plugins. Vuepress provides a number of core-team and community maintained plugins that do everything from enabling a blog on your site to creating a sitemap, creating diagrams using MermaidJS, or displaying your content as a presentation.</p>
<p>See themes and plugins listed on <a href="https://vuepress.tools/">Vuepress.tools</a>.</p>
<h2 id="that-was-easy-time-to-go-crazy">That was easy. Time to go crazy?</h2>
<p>Well, it depends. Vuepress has its positives and not-so-positives.</p>
<p>For starters -</p>
<ol>
<li>Vuepress is easy to get started with. It&rsquo;s out-of-box theme is great and anyone can get started in no time</li>
<li>Provides a great user experience (similar to a single page app)</li>
<li>It is easy to understand for non-techies. Provides a good isolation b/w content creation and presentation layers</li>
<li>Has the full power of Vue behind it - you can provide super interactivity using Vue components and plugins</li>
<li>Theming is quite strong</li>
<li>It has some good plugins. You can also roll out your own plugin quite easily</li>
</ol>
<p>On the other hand..</p>
<ol>
<li>Is not as flexible as more developer-friendly solutions like Gridsome. Yes, this is opinionated and YMMV</li>
<li>I struggled a bit to implement adhoc taxonomies</li>
<li>With dependency on Webpack, it does not lend itself well for lot of content</li>
<li>It has a big initial payload, which is increasingly viewed as something that we live with (but wanted to mention it anyway)</li>
</ol>
<p>Starting a documentation site and using a couple of plugins was easy enough, but my experience customising Vuepress was not that great (again, this is a &lsquo;me&rsquo; experience). I found Gridsome or Saber.land easier to understand and customise to my heart&rsquo;s content.</p>
<p>That said, I will probably use Vuetify for all the documentation sites going forward since it is quite simple and fast to get started with.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Simple Site Summary in Hugo</title>
      <link>https://techformist.com/simple-site-summary-hugo/</link>
      <pubDate>Mon, 25 Nov 2019 18:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/simple-site-summary-hugo/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a simple way to organize your taxonomies and have a bird&amp;rsquo;s eye view of the number of posts per category/tag in Hugo.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;As techformist.com grew, I became more and more disorganised in maintaining taxonomies. I had some interesting situations -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &amp;ldquo;static-site&amp;rdquo; category coexist with &amp;ldquo;static sites&amp;rdquo; category&lt;/li&gt;
&lt;li&gt;confusion b/w why I chose a few terms to be tags rather than categories in their own right&lt;/li&gt;
&lt;li&gt;the recurring need to &amp;ldquo;rethink taxonomy structure&amp;rdquo; depending on the flavour of the month and time of the day&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This often required me to go back and change a few taxonomy terms, or to reorganize site structure in order to provide better visibility to chosen topics.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a simple way to organize your taxonomies and have a bird&rsquo;s eye view of the number of posts per category/tag in Hugo.</p>
<h2 id="the-problem">The Problem</h2>
<p>As techformist.com grew, I became more and more disorganised in maintaining taxonomies. I had some interesting situations -</p>
<ul>
<li>a &ldquo;static-site&rdquo; category coexist with &ldquo;static sites&rdquo; category</li>
<li>confusion b/w why I chose a few terms to be tags rather than categories in their own right</li>
<li>the recurring need to &ldquo;rethink taxonomy structure&rdquo; depending on the flavour of the month and time of the day</li>
</ul>
<p>This often required me to go back and change a few taxonomy terms, or to reorganize site structure in order to provide better visibility to chosen topics.</p>
<p>I also developed this habit of referring back to the older topics with a site search / VSCode search just to copy/paste tags and categories from an older post.</p>
<p>This was frustrating.</p>
<h2 id="the-solution">The Solution</h2>
<p>There were couple of simple solution options -</p>
<ol>
<li>Create a summary page on the site</li>
<li>Include counts in the manually maintained writing/editorial calendar (and bring some automation to that side of the world while at it)</li>
<li>Analyse sitemap to get a nice summary and maintain that analysis somewhere</li>
</ol>
<p>In the end, I felt it would be easier to create a quick summary page on the site and refer back to it, rather than over-engineer a solution.</p>
<p>So, I created this summary page with just a few lines of code for layout.</p>
<p><img loading="lazy" src="/misc/hugo-site-summary.jpg" type="" alt="hugo-summary-count-posts-categories-tags"  /></p>
<p>I can now -</p>
<ol>
<li>Quickly check the categories / tags and select relevant combination for a new post</li>
<li>Keep an eye on the counts and refocus on things that I have to write more about</li>
<li>Satisfy my ego by showing the post counts and by hiding away more useful statistics such as the usefulness &amp; longevity of the content</li>
</ol>
<h2 id="how-to-implement-this-in-your-own-hugo-site">How to implement this in your own Hugo site?</h2>
<p>Create a file called &ldquo;summary.html&rdquo; in your <code>layouts</code> folder.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{ partial &#34;header&#34; . }} 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{ partial &#34;header/header&#34; . }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{/*-- post count */}}
</span></span><span class="line"><span class="cl">{{ $posts := (where .Site.RegularPages &#34;Section&#34; &#34;==&#34; &#34;posts&#34;) }}
</span></span><span class="line"><span class="cl">{{ $postCount := len $posts }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{/*-- page count */}}
</span></span><span class="line"><span class="cl">{{ $pages := (where .Site.RegularPages &#34;Section&#34; &#34;==&#34; &#34;page&#34;) }}
</span></span><span class="line"><span class="cl">{{ $pageCount := len $pages }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;overflow: auto;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">table</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">thead</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">td</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;text-align:center;font-weight: bold;width: 10em;&#34;</span><span class="p">&gt;</span>Description<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">td</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;text-align:center;font-weight: bold;&#34;</span><span class="p">&gt;</span>Value<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">thead</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">tbody</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>Pages<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ $pageCount }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>Posts<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ $postCount }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>Post by Categories<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{ range $name, $taxonomy := .Site.Taxonomies.categories }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/categories/{{ $name | urlize }}&#34;</span><span class="p">&gt;</span>{{ $name | humanize }} ({{ $taxonomy.Count }})<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="ni">&amp;nbsp;</span>
</span></span><span class="line"><span class="cl">          {{end}}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;&lt;</span><span class="nt">td</span><span class="p">&gt;</span>Post by Tags<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{ range $name, $taxonomy := .Site.Taxonomies.tags }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/tags/{{ $name | urlize }}&#34;</span><span class="p">&gt;</span>#{{ $name | humanize }} ({{ $taxonomy.Count }})<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="ni">&amp;nbsp;</span>
</span></span><span class="line"><span class="cl">          {{end}}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">tbody</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">table</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{{ partial &#34;footer&#34; . }}
</span></span></code></pre></div><p>As you can see, I am no where near an expert on Hugo. Or, even basic styling in HTML :)</p>
<p>Next, create a content page in <code>pages</code> folder. This is to better organize structure in the future.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: Summary of Posts
</span></span><span class="line"><span class="cl">url: summary
</span></span><span class="line"><span class="cl">type: page
</span></span><span class="line"><span class="cl">layout: summary
</span></span><span class="line"><span class="cl">date: 2020-02-20 20:20:20
</span></span><span class="line"><span class="cl">comments: false
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is a summary of the site that lists categories, tags and what not alongside the number of posts. Not quite useful for anyone other than the publishers.
</span></span></code></pre></div><p>Navigate to <code>&lt;your site&gt;/summary</code>.</p>
<p>Have fun.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Host your own site - 2020 edition</title>
      <link>https://techformist.com/host-your-site-2020/</link>
      <pubDate>Mon, 18 Nov 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/host-your-site-2020/</guid>
      <description>&lt;p&gt;Medium, Dev.to, Hashnode, Wordpress? Or, why should you invest in yourself rather than in platforms?&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Developers should write and write often. It may be anything - code snippets, ideas, thoughts, design principles, standards and what not. I have found that writing down things often forces me to solidify abstractions and get more clarity.&lt;/p&gt;
&lt;p&gt;But, where to write?&lt;/p&gt;
&lt;h2 id=&#34;the-options&#34;&gt;The Options&lt;/h2&gt;
&lt;p&gt;Wordpress is the past. Though I continue to host a few of my blogs - it is just laziness preventing me from migrating over to better things. Wordpress is often slow, theme design is frustrating (if you are not in WP/PHP world that is), and in general does not carry itself quite well nowadays.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Medium, Dev.to, Hashnode, Wordpress? Or, why should you invest in yourself rather than in platforms?</p>
<h2 id="the-problem">The Problem</h2>
<p>Developers should write and write often. It may be anything - code snippets, ideas, thoughts, design principles, standards and what not. I have found that writing down things often forces me to solidify abstractions and get more clarity.</p>
<p>But, where to write?</p>
<h2 id="the-options">The Options</h2>
<p>Wordpress is the past. Though I continue to host a few of my blogs - it is just laziness preventing me from migrating over to better things. Wordpress is often slow, theme design is frustrating (if you are not in WP/PHP world that is), and in general does not carry itself quite well nowadays.</p>
<p>Medium is pay-walled in a annoying way, and annoys you more with popups and what not. And, it is not a pleasant thing to port stuff.</p>
<p>Dev.to / Hash.node are better. You can write stuff and copy over/export markdown. You should be easily able to port it elsewhere if it comes to it.</p>
<p>So, what should you choose?</p>
<h2 id="the-answer">The Answer</h2>
<p>None.</p>
<p>This is 2020, and you should not be debating on which platform to write on.</p>
<ul>
<li>Just choose a static site generator - Hugo, Jekyll, or 11ty.</li>
<li>Host your content on Github, Netlify, Gitlab, or Amazon S3</li>
<li>Publish same content/summary on dev.to or other platforms if you are looking forward to involve that community in your discussions</li>
</ul>
<p>Want more? See -</p>
<ul>
<li>Pick one of the <a href="https://themes.gohugo.io/">Hugo themes</a> and get started on the fastest static site generator in seconds</li>
<li>Start with Jekyll using a theme - <a href="https://techformist.com/create-beautiful-performant-sites-free-ebook/">https://techformist.com/create-beautiful-performant-sites-free-ebook/</a></li>
<li>Start with <a href="https://www.11ty.dev/docs/getting-started/">11ty</a> using one of the <a href="https://www.11ty.dev/docs/starter/">starter projects</a></li>
</ul>
<h2 id="why-do-this">Why do this?</h2>
<p>Well.. you own your content and your destiny. It is much,much easier to control all parts of your story and depend on third parties for a larger exposure - only when required.</p>
<p>Platforms can be transient. Your stories should not be. There should be one place to record your thoughts through years, and what better way than putting all your content in one place. It is really easy to play around with all that data 5-6 years down the line rather than extracting data from a third-party site in some obscure format.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Include full content in site feed in Hugo</title>
      <link>https://techformist.com/full-post-feed-hugo/</link>
      <pubDate>Thu, 17 Oct 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/full-post-feed-hugo/</guid>
      <description>&lt;p&gt;Include all content of your posts in your site feed when using Hugo static site generator.&lt;/p&gt;
&lt;p&gt;By default Hugo will include only summary in your website feed (e.g. in &lt;code&gt;/index.xml&lt;/code&gt;). Your theme may already provide a way to provide full content instead of just the summary. If it doesn&amp;rsquo;t, you can make those simple changes yourself.&lt;/p&gt;
&lt;p&gt;Create a new file &lt;code&gt;rss.xml&lt;/code&gt; in &lt;code&gt;layouts\_default\&lt;/code&gt; folder in your site root directory. Include following content (as of v0.58) -&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Include all content of your posts in your site feed when using Hugo static site generator.</p>
<p>By default Hugo will include only summary in your website feed (e.g. in <code>/index.xml</code>). Your theme may already provide a way to provide full content instead of just the summary. If it doesn&rsquo;t, you can make those simple changes yourself.</p>
<p>Create a new file <code>rss.xml</code> in <code>layouts\_default\</code> folder in your site root directory. Include following content (as of v0.58) -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="c">&lt;!-- layouts\_default\rss.xml --&gt;</span>
</span></span><span class="line"><span class="cl">{{ printf &#34;<span class="cp">&lt;?xml version=\&#34;1.0\&#34; encoding=\&#34;utf-8\&#34; standalone=\&#34;yes\&#34; ?&gt;</span>&#34; | safeHTML }}
</span></span><span class="line"><span class="cl"><span class="nt">&lt;rss</span> <span class="na">version=</span><span class="s">&#34;2.0&#34;</span> <span class="na">xmlns:atom=</span><span class="s">&#34;http://www.w3.org/2005/Atom&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;channel&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;title&gt;</span>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}<span class="nt">&lt;/title&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;link&gt;</span>{{ .Permalink }}<span class="nt">&lt;/link&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;description&gt;</span>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}<span class="nt">&lt;/description&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;generator&gt;</span>Hugo -- gohugo.io<span class="nt">&lt;/generator&gt;</span>{{ with .Site.LanguageCode }}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;language&gt;</span>{{.}}<span class="nt">&lt;/language&gt;</span>{{end}}{{ with .Site.Author.email }}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;managingEditor&gt;</span>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}<span class="nt">&lt;/managingEditor&gt;</span>{{end}}{{ with .Site.Author.email }}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;webMaster&gt;</span>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}<span class="nt">&lt;/webMaster&gt;</span>{{end}}{{ with .Site.Copyright }}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;copyright&gt;</span>{{.}}<span class="nt">&lt;/copyright&gt;</span>{{end}}{{ if not .Date.IsZero }}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;lastBuildDate&gt;</span>{{ .Date.Format &#34;2006-01-02&#34; | safeHTML }}<span class="nt">&lt;/lastBuildDate&gt;</span>{{ end }}
</span></span><span class="line"><span class="cl">    {{ with .OutputFormats.Get &#34;RSS&#34; }}
</span></span><span class="line"><span class="cl">        {{ printf &#34;<span class="nt">&lt;atom:link</span> <span class="na">href=</span><span class="s">%q</span> <span class="na">rel=</span><span class="s">\&#34;self\&#34;</span> <span class="na">type=</span><span class="s">%q</span> <span class="nt">/&gt;</span>&#34; .Permalink .MediaType | safeHTML }}
</span></span><span class="line"><span class="cl">    {{ end }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    {{- range first 10 (where .Site.RegularPages &#34;Type&#34; &#34;not in&#34; &#34;pages&#34; ) -}}
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;item&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;title&gt;</span>{{ .Title }}<span class="nt">&lt;/title&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;link&gt;</span>{{ .Permalink }}<span class="nt">&lt;/link&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;pubDate&gt;</span>{{ .Date.Format &#34;2006-01-02&#34; | safeHTML }}<span class="nt">&lt;/pubDate&gt;</span>
</span></span><span class="line"><span class="cl">      {{ with .Site.Author.email }}<span class="nt">&lt;author&gt;</span>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}<span class="nt">&lt;/author&gt;</span>{{end}}
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;guid&gt;</span>{{ .Permalink }}<span class="nt">&lt;/guid&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;description&gt;</span>{{ .Content | html }}<span class="nt">&lt;/description&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/item&gt;</span>
</span></span><span class="line"><span class="cl">    {{ end }}
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/channel&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/rss&gt;</span>
</span></span></code></pre></div><p>The content of the file is almost same as the <a href="https://gohugo.io/templates/rss/#the-embedded-rss-xml">embedded <code>rss.xml</code> used in Hugo</a>.</p>
<p>.. except for two crucial differences -</p>
<ol>
<li>
<p>Include full content in RSS feed</p>
<pre tabindex="0"><code>&lt;description&gt;{{ .Content | html }}&lt;/description&gt;
</code></pre></li>
<li>
<p>Fetch only 10 articles in the feed. You can get this 10 from the site config as well.</p>
<pre tabindex="0"><code>{{- range first 10 (where .Site.RegularPages &#34;Type&#34; &#34;not in&#34; &#34;pages&#34; ) -}}
</code></pre></li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Crazy space utilization in VSCode</title>
      <link>https://techformist.com/clean-temporary-storage-vscode/</link>
      <pubDate>Mon, 07 Oct 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/clean-temporary-storage-vscode/</guid>
      <description>&lt;p&gt;Running out of space? You have VSCode and love tinkering with it? Then do this.&lt;/p&gt;
&lt;p&gt;Well the introductory line is cheesy and shows desperation for clicks. So, I had to have them.&lt;/p&gt;
&lt;p&gt;And - no, this is not even a secret. VSCode stays sane until it goes insane with storage space. But somehow I did not know my own drive utilization for a long, long time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Caution: Before you start deleting things.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Running out of space? You have VSCode and love tinkering with it? Then do this.</p>
<p>Well the introductory line is cheesy and shows desperation for clicks. So, I had to have them.</p>
<p>And - no, this is not even a secret. VSCode stays sane until it goes insane with storage space. But somehow I did not know my own drive utilization for a long, long time.</p>
<blockquote>
<p>Caution: Before you start deleting things.</p>
<p>Take note that **<em>I</em>** did not find any problems doing the below remedial actions. Your mileage may vary and for all you know, deleting your folder may destroy civilization as we know it.</p>
</blockquote>
<p>You should see this entire article of 2 lines, delete to recycle bin so that you can recover stuff if things go wrong, and, in general, be a good Internet citizen.</p>
<p>To clean up the storage -</p>
<pre tabindex="0"><code>1. Go to C:\Users\&lt;user&gt;\AppData\Roaming\Code\User\workspaceStorage
2. Delete all folders created before last week
</code></pre><p>See GBs of space recovered within no time. Rejoice.</p>
<h3 id="so-whats-up-with-this-folder">So, what&rsquo;s up with this folder?</h3>
<p>Appears to be a bug but on nobody&rsquo;s radar.</p>
<p>The workspace folder fills up pretty fast if you are using C++ extensions. Even otherwise, for a VSCode enthusiast like me who tries multiple extensions, uses more than one language and uses multiple themes - the folder can become overwhelming.</p>
<p>How overwhelming? I had a 128 GB primary partition and ~9 GB of that was taken by the said folder. One week after the clean up - the space utilization for the sub-folder remains at &lsquo;&lt; 20 MB&rsquo;.</p>
<p>The folder is used as a temporary storage location. Anything you delete should get recreated as you start using the program/extension again.</p>
<h3 id="bug-what-bug">Bug, what bug?</h3>
<p>See -</p>
<ul>
<li><a href="https://github.com/microsoft/vscode/issues/32461">Clean up ExtensionContext.workspaceState when a workspace no longer exists</a></li>
<li><a href="https://github.com/Microsoft/vscode/issues/53552">Why is my workspace storage so big?</a></li>
<li><a href="https://github.com/Microsoft/vscode/issues/39692">Can I clean storage.. ?</a></li>
<li><a href="https://github.com/Microsoft/vscode/issues/22557">Setting to change workspaceStorage location </a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Commit to blog daily - it&#39;s smart and stupid</title>
      <link>https://techformist.com/2019/09/30/commit-to-blog-daily-its-smart-and-stupid/</link>
      <pubDate>Mon, 30 Sep 2019 18:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/2019/09/30/commit-to-blog-daily-its-smart-and-stupid/</guid>
      <description>&lt;p&gt;Daily blogging by itself is not a bad thing.&lt;/p&gt;
&lt;p&gt;I changed tracks a year back to get back to development after a million years of doing something else. I thought through the idea of blogging daily, what I was going to write about, and how that would pan out through days and months.&lt;/p&gt;
&lt;p&gt;The premise was simple - learn something, and record that for yourself. The world may benefit from it if it chooses to.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Daily blogging by itself is not a bad thing.</p>
<p>I changed tracks a year back to get back to development after a million years of doing something else. I thought through the idea of blogging daily, what I was going to write about, and how that would pan out through days and months.</p>
<p>The premise was simple - learn something, and record that for yourself. The world may benefit from it if it chooses to.</p>
<p>The idea worked out well at the beginning. I started a new blog at <a href="https://techformist.com">techformist.com</a>, and was fairly regular (if not daily).</p>
<ol>
<li>Thanks to my tech background, I had a few things that I could write about from the very beginning</li>
<li>I was learning at a quicker pace than anticipated. So, I was getting &ldquo;more stuff&rdquo; all the time</li>
</ol>
<p>But, as it is with all things with humans, things slowed down.</p>
<p>I faced the same challenges that I faced elsewhere - I missed out on stuff that I thought was trivial, I regularly failed to record things that I was doing so that I can recall them for future topics, and caught myself worrying about what would be the next big topic to write about.</p>
<p>I could see clear challenges in sticking to the schedule. In hindsight, I should have known better - especially with my &ldquo;experience&rdquo; of blogging since a decade on wide range of topics incl. internet marketing, CRM, gardening, humour and more.</p>
<p>But, what was different this time was my stupid determination to see it through. I took a vow to continue with the daily blog for an year (target: 2019-10-15), and re-evaluate the state of affairs at that time.</p>
<p>I took breaks in between running a new company just to stick to schedule. I went completely berserk several times in the last two months to &ldquo;fill in the gap days&rdquo;, publish/complete my often incomplete drafts, and fulfil my obsession about this blog being a &ldquo;daily blog&rdquo;. I scourged my code for newer tips that I could write about, compared notes with Reddit / Stackoverflow / and even Quora to check what people were asking about, looked at what other daily bloggers were doing, and did everything except visiting a mental health professional.</p>
<p>I wrote-in the topic for 15-Oct last week, and despite loving what I do - I heaved a sign of relief and have made up my mind to stop the current way.</p>
<p>Here&rsquo;s a run down of things that went right and wrong out of 365 days of blogging.</p>
<h3 id="what-went-right">What went right?</h3>
<ul>
<li>
<p>I forced myself to really &ldquo;see&rdquo; many things that were otherwise not paid attention to in a &ldquo;hectic development schedule&rdquo;. For example: <a href="/simpler-curry-notation-javascript/">currying in Javascript</a>, <a href="/reusable-memoization-function-javascript/">reusable functions for memoization</a> or <a href="/map-vs-object-javascript/">why would someone use maps vis-a-vis an object</a>.</p>
</li>
<li>
<p>to an extent - I could hold myself accountable to the wider world to churn out new stuff ( world = those who dared to read the blog). Even if the said stuff meant little to them over the course of time. For example, <a href="/array-manual-sort-javascript/">bubble sort</a>, or <a href="/fibonacci-series-using-generator-javascript/">more</a> <a href="/memoization-javascript/">than</a> a <a href="/reusable-memoization-function-javascript/">few</a> posts about how to generate Fibonacci series.</p>
</li>
<li>
<p>I proved to myself for the umpteenth time that I could stick to boring things just so to see it completed</p>
</li>
</ul>
<h3 id="what-went-wrong">What went wrong?</h3>
<p>Technically, nothing. But practically -</p>
<ul>
<li>Quality suffered at the expense of number of posts. This should not be the case in an ideal world, but I am not a member of one</li>
<li>Topics tend to focus on solving something small - my whole purpose on the blog seemed to be focused on &ldquo;satisfying the quota&rdquo;. This is a wrong approach for learning or for sharing of anything worth sharing. Long-form topics were shunned if I had some catching up to do on the daily blog requirements - and this was (seemingly) all the time</li>
<li>It is ok to blog for self. But you cannot hold attention of readers on small, seemingly adhoc topics (even though the thread of web development holds everything together). Long-form posts are loved and respected by both readers, and as a consequence, by search engines. People read through at the posts for a longer time, seem to appreciate the deeper coverage a bit more, click-through to browse a few other topics, and possibly subscribe to future updates anticipating similar value</li>
<li>It is ok to solve a small problem in a post, but very few readers stick on to browse through other topics</li>
<li>I should have followed up my daily blog with a book, videos or something else. This did not quite happen in the same way that I envisioned it to happen</li>
</ul>
<p>None of these points are anything new (even for a being like me) but I derive satisfaction in looking back, seeing the bigger picture and to validate, yet again, that the tract of &lsquo;humanity repeating itself&rsquo; is intact.</p>
<h3 id="whats-in-it-for-you">What&rsquo;s in it for you?</h3>
<p>Take-aways for anyone interested in the topic -</p>
<ul>
<li>You will enjoy blogging daily if you are interested in the topic. You will subject yourself to suffering if you hold yourself accountable to it</li>
<li>Prefer long-form posts and deliver more value within a single post rather than splitting things in multiple posts just for the sake of it</li>
<li>Daily tips are ok, but don&rsquo;t be afraid to repeat them at the risk of infuriating your loyal readers. Daily blogs are best run through email if you are looking forward to taking your message to the world</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Add PWA to your Hugo site</title>
      <link>https://techformist.com/add-pwa-hugo/</link>
      <pubDate>Wed, 11 Sep 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/add-pwa-hugo/</guid>
      <description>&lt;p&gt;Adding PWA to your Hugo static site is quite easy.&lt;/p&gt;
&lt;h3 id=&#34;what-is-pwa-and-why-should-i-add-it&#34;&gt;What is PWA and why should I add it?&lt;/h3&gt;
&lt;p&gt;Progressive web applications (PWA) are a nice way to give your websites an &amp;ldquo;app makeover&amp;rdquo;. Using PWAs your sites are perceived to load faster, are available offline, and in general improve user experience.&lt;/p&gt;
&lt;p&gt;PWAs are supported by all web browsers. Using PWAs on single page applications have shown remarkable improvement in site speed and have provided us the ability to continuing to serve static content even when the connectivity is down.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Adding PWA to your Hugo static site is quite easy.</p>
<h3 id="what-is-pwa-and-why-should-i-add-it">What is PWA and why should I add it?</h3>
<p>Progressive web applications (PWA) are a nice way to give your websites an &ldquo;app makeover&rdquo;. Using PWAs your sites are perceived to load faster, are available offline, and in general improve user experience.</p>
<p>PWAs are supported by all web browsers. Using PWAs on single page applications have shown remarkable improvement in site speed and have provided us the ability to continuing to serve static content even when the connectivity is down.</p>
<p>Back to Hugo - although I can see incremental gains, nothing jumped out to make up for a compelling case for PWA. The sites generated by Hugo are static - pages are already generated and waiting to be loaded. Depending on the theme, you may already be seeing the best load times that your site can achieve. So, YMMV.</p>
<h3 id="how-do-i-add-pwa-to-hugo">How do I add PWA to Hugo?</h3>
<p>At a minimum a PWA needs a manifest and service worker. We will use a offline-first cache strategy to enable PWA.</p>
<h5 id="create-your-icons">Create your icons</h5>
<p>PWAs are available offline and can be installed on the phone. This requires icons of different sizes for your website. Just pluck your logo, remove the text if necessary and generate icons of all popular sizes. You can use a service like <a href="https://favicomatic.com/">Favicomatic</a> to make that a one-click process.</p>
<h5 id="create-manifest">Create manifest</h5>
<p>Create a new file called <code>manifest.json</code> in your <code>/static</code> folder and add content similar to below</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Techformist&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;short_name&#34;</span><span class="p">:</span> <span class="s2">&#34;techformist&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;icons&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;src&#34;</span><span class="p">:</span> <span class="s2">&#34;/appicons/favicon-128.png&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sizes&#34;</span><span class="p">:</span> <span class="s2">&#34;128x128&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;src&#34;</span><span class="p">:</span> <span class="s2">&#34;/appicons/apple-touch-icon-144x144.png&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sizes&#34;</span><span class="p">:</span> <span class="s2">&#34;144x144&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;src&#34;</span><span class="p">:</span> <span class="s2">&#34;/appicons/apple-touch-icon-152x152.png&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sizes&#34;</span><span class="p">:</span> <span class="s2">&#34;152x152&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;src&#34;</span><span class="p">:</span> <span class="s2">&#34;/appicons/favicon-196x196.png&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sizes&#34;</span><span class="p">:</span> <span class="s2">&#34;196x196&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;src&#34;</span><span class="p">:</span> <span class="s2">&#34;/appicons/splash.png&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sizes&#34;</span><span class="p">:</span> <span class="s2">&#34;512x512&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;start_url&#34;</span><span class="p">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;display&#34;</span><span class="p">:</span> <span class="s2">&#34;standalone&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;orientation&#34;</span><span class="p">:</span> <span class="s2">&#34;portrait&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;background_color&#34;</span><span class="p">:</span> <span class="s2">&#34;#FFFFFF&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;theme_color&#34;</span><span class="p">:</span> <span class="s2">&#34;#FFFFFF&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h5 id="create-service-worker">Create service worker</h5>
<p>Create a new javascript file <code>sw.js</code> in <code>/static</code>. This script does all the heavy lifting like maintaining cache and enabling your site to be treated like an app.</p>
<p>I just copied the code from the sample service worker script available at [https://github.com/wildhaber/offline-first-sw].</p>
<p>Modify the file and choose to make some or all files available offline - I typically include CSS, common Javascript files and images. See the end of this article for the file used by this blog.</p>
<h5 id="include-manifest-in-head">Include manifest in <code>head</code></h5>
<p>Include a link to your manifest in your header.</p>
<p>I use Minimo theme and included the following line in <code>&lt;root&gt;/layouts/partials/head/extra.html</code>. Note that I am not over-writing the file in the theme folder, but overriding it using my own version (as is typical to Hugo themes).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;manifest&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/manifest.json&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></div><h5 id="include-sw-in-footer">Include SW in footer</h5>
<p>Include the service worker JS file reference in your footer. For some strange reason, I chose to be different and included that in my <code>sidebar.html</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="s1">&#39;serviceWorker&#39;</span> <span class="k">in</span> <span class="nx">navigator</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="s1">&#39;/sw.js&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">scope</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">registration</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">//console.log(&#39;Service Worker Registered&#39;);
</span></span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">ready</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">registration</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">//console.log(&#39;Service Worker Ready&#39;);
</span></span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="see-the-results">See the Results</h3>
<p>That is it. Your site automagically is a PWA now.</p>
<p>Check out Chrome developer tools -</p>
<h5 id="a-site-or-an-app">A site or an app?</h5>
<p>Go to <code>Application</code> tab.</p>
<p>You should see details provided in your manifest including the different icons. You will also see the different offline files you specified in the <code>Cache Storage</code> section.</p>
<h5 id="lighthouse-scores">Lighthouse scores</h5>
<p>Go to <code>Audit</code> tab and do a Lighthouse audit for your site.</p>
<p>Mobile Lighthouse score for home page -</p>
<p><img loading="lazy" src="/misc/lighthouse-score-home-page.jpg" type="" alt="lighthouse-score-home-page"  /></p>
<p>Lighthouse score for an article -</p>
<p><img loading="lazy" src="/misc/lighthouse-score-article.jpg" type="" alt="lighthouse-score-article"  /></p>
<p>Rejoice.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Adding and setting up PWA to Hugo is trivial. But, I did not clearly make out any significant advantages of enabling PWA on my blog - given that I am not after providing a read-offline feature.</p>
<h3 id="post-script">Post Script</h3>
<h5 id="swjs-code">sw.js code</h5>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">CACHE_VERSION</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">BASE_CACHE_FILES</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/css/custom.css&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/js/custom.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/search/index.json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/manifest.json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/favicon.png&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/images/logo.png&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/techformist-logo-no-text.png&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/css/main.6a060eb7.css&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/main.67d669ac.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/sidebar.9ea42a6e.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/fuse_search.1ada4bca.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">OFFLINE_CACHE_FILES</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/images/logo.png&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/techformist-logo-no-text.png&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/css/main.6a060eb7.css&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/main.67d669ac.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/sidebar.9ea42a6e.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/assets/js/fuse_search.1ada4bca.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NOT_FOUND_CACHE_FILES</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/404.html&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">OFFLINE_PAGE</span> <span class="o">=</span> <span class="s1">&#39;/offline/index.html&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">NOT_FOUND_PAGE</span> <span class="o">=</span> <span class="s1">&#39;/404.html&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">CACHE_VERSIONS</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">assets</span><span class="o">:</span> <span class="s1">&#39;assets-v&#39;</span> <span class="o">+</span> <span class="nx">CACHE_VERSION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">content</span><span class="o">:</span> <span class="s1">&#39;content-v&#39;</span> <span class="o">+</span> <span class="nx">CACHE_VERSION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">offline</span><span class="o">:</span> <span class="s1">&#39;offline-v&#39;</span> <span class="o">+</span> <span class="nx">CACHE_VERSION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">notFound</span><span class="o">:</span> <span class="s1">&#39;404-v&#39;</span> <span class="o">+</span> <span class="nx">CACHE_VERSION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Define MAX_TTL&#39;s in SECONDS for specific file extensions
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">MAX_TTL</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;/&#39;</span><span class="o">:</span> <span class="mi">3600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">html</span><span class="o">:</span> <span class="mi">3600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">json</span><span class="o">:</span> <span class="mi">86400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">js</span><span class="o">:</span> <span class="mi">86400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">css</span><span class="o">:</span> <span class="mi">86400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">CACHE_BLACKLIST</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="k">return</span> <span class="o">!</span><span class="nx">str</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="s1">&#39;http://localhost&#39;</span><span class="p">)</span> <span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SUPPORTED_METHODS</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * isBlackListed
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param {string} url
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {boolean}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">isBlacklisted</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="nx">CACHE_BLACKLIST</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="o">!</span><span class="nx">CACHE_BLACKLIST</span><span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">rule</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">rule</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">!</span><span class="nx">rule</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}).</span><span class="nx">length</span> <span class="o">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * getFileExtension
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param {string} url
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {string}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getFileExtension</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">extension</span> <span class="o">=</span> <span class="nx">url</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">).</span><span class="nx">reverse</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;?&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="nx">extension</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">))</span> <span class="o">?</span> <span class="s1">&#39;/&#39;</span> <span class="o">:</span> <span class="nx">extension</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * getTTL
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param {string} url
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getTTL</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">url</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">extension</span> <span class="o">=</span> <span class="nx">getFileExtension</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">MAX_TTL</span><span class="p">[</span><span class="nx">extension</span><span class="p">]</span> <span class="o">===</span> <span class="s1">&#39;number&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">MAX_TTL</span><span class="p">[</span><span class="nx">extension</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * installServiceWorker
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {Promise}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">installServiceWorker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">assets</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span><span class="nx">BASE_CACHE_FILES</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">offline</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span><span class="nx">OFFLINE_CACHE_FILES</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">notFound</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">addAll</span><span class="p">(</span><span class="nx">NOT_FOUND_CACHE_FILES</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * cleanupLegacyCache
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {Promise}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">cleanupLegacyCache</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">currentCaches</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">map</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="nx">CACHE_VERSIONS</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nx">caches</span><span class="p">.</span><span class="nx">keys</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">keys</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="nx">legacyKeys</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                            <span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="k">return</span> <span class="o">!~</span><span class="nx">currentCaches</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">legacy</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span> <span class="p">(</span><span class="nx">legacy</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="nx">legacy</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">(</span><span class="nx">legacyKey</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">legacyKey</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="nx">resolve</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="nx">reject</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="p">);</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="nx">resolve</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">reject</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;install&#39;</span><span class="p">,</span> <span class="nx">event</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span><span class="nx">installServiceWorker</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// The activate handler takes care of cleaning up old caches.
</span></span></span><span class="line"><span class="cl"><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;activate&#39;</span><span class="p">,</span> <span class="nx">event</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">event</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="p">[</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">cleanupLegacyCache</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                <span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">event</span><span class="p">.</span><span class="nx">skipWaiting</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;fetch&#39;</span><span class="p">,</span> <span class="nx">event</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">event</span><span class="p">.</span><span class="nx">respondWith</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="nx">cache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                    <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                        <span class="kd">let</span> <span class="nx">headers</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">entries</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                                        <span class="kd">let</span> <span class="nx">date</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                        <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">pair</span> <span class="k">of</span> <span class="nx">headers</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                            <span class="k">if</span> <span class="p">(</span><span class="nx">pair</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="s1">&#39;date&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                <span class="nx">date</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nx">pair</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                        <span class="k">if</span> <span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                            <span class="kd">let</span> <span class="nx">age</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">((</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">-</span> <span class="nx">date</span><span class="p">.</span><span class="nx">getTime</span><span class="p">())</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                            <span class="kd">let</span> <span class="nx">ttl</span> <span class="o">=</span> <span class="nx">getTTL</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                            <span class="k">if</span> <span class="p">(</span><span class="nx">ttl</span> <span class="o">&amp;&amp;</span> <span class="nx">age</span> <span class="o">&gt;</span> <span class="nx">ttl</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                    <span class="p">(</span><span class="nx">resolve</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                        <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                                <span class="p">(</span><span class="nx">updatedResponse</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                                    <span class="k">if</span> <span class="p">(</span><span class="nx">updatedResponse</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                                        <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">,</span> <span class="nx">updatedResponse</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">                                                                        <span class="nx">resolve</span><span class="p">(</span><span class="nx">updatedResponse</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                                                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                                        <span class="nx">resolve</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                                <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                                    <span class="nx">resolve</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                    <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                            <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                    <span class="p">);</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                            <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                        <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">                                            <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                    <span class="k">if</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span> <span class="o">&lt;</span> <span class="mi">400</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                        <span class="k">if</span> <span class="p">(</span><span class="o">~</span><span class="nx">SUPPORTED_METHODS</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">method</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">isBlacklisted</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                            <span class="nx">cache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                        <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                                    <span class="p">}</span> 
</span></span><span class="line"><span class="cl">                                                    <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                        <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">notFound</span><span class="p">).</span><span class="nx">then</span><span class="p">((</span><span class="nx">cache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                            <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">NOT_FOUND_PAGE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">})</span>
</span></span><span class="line"><span class="cl">                                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                <span class="k">if</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                    <span class="k">return</span> <span class="nx">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">})</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                    <span class="k">return</span> <span class="nx">caches</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">CACHE_VERSIONS</span><span class="p">.</span><span class="nx">offline</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">.</span><span class="nx">then</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">(</span><span class="nx">offlineCache</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                                                <span class="k">return</span> <span class="nx">offlineCache</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">OFFLINE_PAGE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                                        <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                                        
</span></span><span class="line"><span class="cl">                                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                            <span class="p">)</span>
</span></span><span class="line"><span class="cl">                            <span class="p">.</span><span class="k">catch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;  Error in fetch handler:&#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                            <span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Prevent test emails going out to users in AdonisJS</title>
      <link>https://techformist.com/prevent-test-emails-adonisjs/</link>
      <pubDate>Mon, 09 Sep 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/prevent-test-emails-adonisjs/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a quick way to prevent emails from going out in test environments in AdonisJS.&lt;/p&gt;
&lt;p&gt;We typically end up getting production data in part or in whole to test environments for a &amp;ldquo;proper&amp;rdquo; round of testing on real data. But all the data attributes cannot be so &amp;ldquo;real&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;We typically end up changing fields like emails so that customers do not start receiving emails from non-production environments.&lt;/p&gt;
&lt;p&gt;In addition to the above change, we try to specify an explicit opt-in to communications so that people who are not associated with testing do not get confused with test transactions - even when the said people are within the client organization.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a quick way to prevent emails from going out in test environments in AdonisJS.</p>
<p>We typically end up getting production data in part or in whole to test environments for a &ldquo;proper&rdquo; round of testing on real data. But all the data attributes cannot be so &ldquo;real&rdquo;.</p>
<p>We typically end up changing fields like emails so that customers do not start receiving emails from non-production environments.</p>
<p>In addition to the above change, we try to specify an explicit opt-in to communications so that people who are not associated with testing do not get confused with test transactions - even when the said people are within the client organization.</p>
<p>One of the fields impacted is the contact email (or login , if the application uses login id as email). I typically use <code>@adonisjs/mail</code> module to send emails and implement a quick opt-in filter to prevent emails from going out to anyone other than those specified in a list.</p>
<p>Another option that enables better testing is to pre-identify one or more test users, and redirect all emails from the test environment to those users. This will enable them to pick and chose any deal, contact or other records in testing and get notifications to their email ids. The script for such mail redirection to whitelisted testers is similar to the block below -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// Services/SendMailService.js
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s2">&#34;use strict&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Mail</span> <span class="o">=</span> <span class="nx">use</span><span class="p">(</span><span class="s2">&#34;Mail&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Env</span> <span class="o">=</span> <span class="nx">use</span><span class="p">(</span><span class="s2">&#34;Env&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">SendMailService</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">async</span> <span class="nx">sendMail</span><span class="p">({</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">params</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// NO EXTERNAL EMAILS TO BE SENT IN NON-PROD UNLESS WHITELISTED.
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Env</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;NODE_ENV&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s2">&#34;production&#34;</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Env</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;EMAIL_WHITE_LIST&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">email</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">data</span><span class="p">.</span><span class="nx">email</span> <span class="o">=</span> <span class="nx">Env</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;EMAIL_TEST_USER&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">params</span><span class="p">.</span><span class="nx">template</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">template</span> <span class="o">?</span> <span class="nx">params</span><span class="p">.</span><span class="nx">template</span> <span class="o">:</span> <span class="s2">&#34;hello&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">params</span><span class="p">.</span><span class="nx">subject</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">subject</span> <span class="o">?</span> <span class="nx">params</span><span class="p">.</span><span class="nx">subject</span> <span class="o">:</span> <span class="nx">params</span><span class="p">.</span><span class="nx">template</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">Mail</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">&#34;emails.&#34;</span> <span class="o">+</span> <span class="nx">params</span><span class="p">.</span><span class="nx">template</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">message</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">message</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">to</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">Env</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;SMTP_USERNAME&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nx">subject</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">subject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SendMailService</span><span class="p">();</span>
</span></span></code></pre></div><p>We are doing three simple things here -</p>
<ul>
<li>check whether environment is production using <code>NODE_ENV</code>. We let the request go as-is if it&rsquo;s production</li>
<li>else, check whether the user is part of ids specified against <code>EMAIL_WHITE_LIST</code> in <code>.env</code> file. Let the email go if the id is white-listed</li>
<li>else, send email to the ids specified against <code>EMAIL_TEST_USER</code> in <code>.env</code> file</li>
</ul>
<p>Since <code>.env</code> are specific to the environment, we can control the test users and white listing for different test environments.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Production to test migration strategy</title>
      <link>https://techformist.com/production-test-migration-strategy/</link>
      <pubDate>Sun, 08 Sep 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/production-test-migration-strategy/</guid>
      <description>&lt;p&gt;While we love to have production-like environments for user testing, managing data and the test environments can present unique challenges.&lt;/p&gt;
&lt;p&gt;In today&amp;rsquo;s enterprises it is fairly common to have multiple test environments including staging, system / user testing environments and so on. These environments expect production-like features but only for specified users.&lt;/p&gt;
&lt;p&gt;For example -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Validations have to work as they are in production&lt;/li&gt;
&lt;li&gt;Document templates and document merge has to work&lt;/li&gt;
&lt;li&gt;Email and SMS communications have to work for select users&lt;/li&gt;
&lt;li&gt;Logins have to work for select users&lt;/li&gt;
&lt;li&gt;A few staging environments may need data in volume - what better data can you find outside of your own production environment?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As is the norm for typical enterprise applications, I tend to -&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>While we love to have production-like environments for user testing, managing data and the test environments can present unique challenges.</p>
<p>In today&rsquo;s enterprises it is fairly common to have multiple test environments including staging, system / user testing environments and so on. These environments expect production-like features but only for specified users.</p>
<p>For example -</p>
<ol>
<li>Validations have to work as they are in production</li>
<li>Document templates and document merge has to work</li>
<li>Email and SMS communications have to work for select users</li>
<li>Logins have to work for select users</li>
<li>A few staging environments may need data in volume - what better data can you find outside of your own production environment?</li>
</ol>
<p>As is the norm for typical enterprise applications, I tend to -</p>
<ul>
<li>keep environments isolated</li>
<li>build environments on specific branches on demand</li>
<li>copy over whatever user configuration and user data that are relevant from production</li>
</ul>
<p>While our build scripts make the build process really easy, the last item for data presents a problem.</p>
<p>For one, we may have sensitive data that has no place in test environments. This data is not meant to be played around by developers, testers and test users.</p>
<p>The second problem is to isolate our real customers from whatever plight awaits their data in test environments. For example, what happens in test environments should stay in test environment, and is not visible/have zero impact on anyone who is not associated with testing.</p>
<p>I try to work-around the problem of data and configuration management post-migration by (trying to) maintain and deploy a bunch of DB/server-side scripts to -</p>
<ol>
<li>Change sensitive fields like emails, and phone numbers</li>
<li>Change encryption keys if required and dummify data in pre-identified fields like personal health information</li>
<li>Scramble logins and passwords to something different</li>
</ol>
<p>All this and more is done post data &amp; configuration migration to target environments.</p>
<p>While all the setup activities need to be automated and be part of test migration scripts, I have never really gotten to the nirvana stage here.</p>
<p>Most of the times I end up doing some manual work and stitch together bit and pieces to -</p>
<ul>
<li>migrate a part of a huge data-set with quirky data distribution requirements</li>
<li>migrate specific parts of production while merging that with parts from other environments</li>
</ul>
<p>I can blame anyone and everyone for that, but at the end of the day, problem lies in my own improper budgeting. Having a lean team (or I alone) focused on a particular solution or project does not help. While the limited people, time and budget gets the work done really quickly and efficiently, there are essentials such as migrations that move to a &lsquo;quasi-essentials&rsquo; bucket, and do not get the attention they deserve.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>What is GraphQL and why should I use it?</title>
      <link>https://techformist.com/why-use-graphql/</link>
      <pubDate>Wed, 21 Aug 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/why-use-graphql/</guid>
      <description>&lt;p&gt;Why do I use GraphQL? Also, the case for not using GraphQL.&lt;/p&gt;
&lt;p&gt;You must have seen the previous post where I waxed eloquent about &lt;a href=&#34;https://techformist.com/not-designing-rest-services&#34;&gt;how I mess up my Rest APIs&lt;/a&gt;. There was no way I would be satisfied doing that to a single technology stack.&lt;/p&gt;
&lt;p&gt;Ergo, GraphQL.&lt;/p&gt;
&lt;h3 id=&#34;what-is-the-need-for-graphql&#34;&gt;What is the need for GraphQL?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.google.com/search?q=graphql&#34;&gt;Google GraphQL&lt;/a&gt; and refer &lt;a href=&#34;https://graphql.org/learn/&#34;&gt;docs&lt;/a&gt;- there are smarter people with better answers than me.&lt;/p&gt;
&lt;p&gt;But, since the Internet can be a scary place and I love to write about things that I am not an expert in - here&amp;rsquo;s a gist about what I think.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Why do I use GraphQL? Also, the case for not using GraphQL.</p>
<p>You must have seen the previous post where I waxed eloquent about <a href="/not-designing-rest-services">how I mess up my Rest APIs</a>. There was no way I would be satisfied doing that to a single technology stack.</p>
<p>Ergo, GraphQL.</p>
<h3 id="what-is-the-need-for-graphql">What is the need for GraphQL?</h3>
<p><a href="https://www.google.com/search?q=graphql">Google GraphQL</a> and refer <a href="https://graphql.org/learn/">docs</a>- there are smarter people with better answers than me.</p>
<p>But, since the Internet can be a scary place and I love to write about things that I am not an expert in - here&rsquo;s a gist about what I think.</p>
<p>Back to the story..</p>
<blockquote>
<p>GraphQL is the SQL for APIs.</p>
</blockquote>
<p>Before you say .. &lsquo;ah.. I now know everything!&rsquo;, read on.</p>
<p>You see - most of the transactions on the web involve fetching some data or the other.</p>
<p>Clients need to be authenticated, authorised and then request data for those transactions to happen. Servers, which supply the data, will comply with client requests based on pre-defined rules.</p>
<p>Rest is an architectural style that allows you to standardize and streamline client-server requests. You can control resources (accounts, contacts etc.) and the operations (GET, STORE etc.).</p>
<p>So far so good. You may even <em>think</em> that you reached a Nirvana state with what has been described so far, but imagine what would happen if you start a series of requests -</p>
<pre tabindex="0"><code>You: Give me contacts
Server: Here are 100 contacts with 501 fields per contact

You: Give me contacts and activities for each contact
Server: Here are 1000 contacts and 1 million activities per contact

You: Give me contact detail
Server: Here you go - 1000 fields of the contact
You: Give me address for same contact
Server: Oh God, you again? Here&#39;s the address. And, can you please stop this madness?
</code></pre><p>You will see such examples in the real world - the problems described are referred as &lsquo;over-fetching&rsquo; (getting more data than I need) and &lsquo;under-fetching&rsquo; (getting less data than I need).</p>
<p>The properties of over-fetching -</p>
<ul>
<li>server fetches more data from database (or may query for related entities that are not always required)</li>
<li>more data is pushed through the pipe</li>
<li>client gets more data than required and has to process data to selectively pick and display data.</li>
</ul>
<p>And, those for under-fetching -</p>
<ul>
<li>client does not require all data in one-go, requests for related entities or fields</li>
<li>additional network requests and more needless traffic</li>
<li>more time to render UI with complete data</li>
</ul>
<p>The back &amp; forth between server and client frustrates the server - as seen in above conversation, and more importantly, impacts client-side performance.</p>
<p>The multiple back and forth requests make Rest &ldquo;chatty&rdquo;.</p>
<p>Ergo, GraphQL.</p>
<p>(I assure you - Matrix has very little to do with me using &rsquo;ergo&rsquo; all the time.)</p>
<h3 id="really-why-do-i-use-graphql">Really, why do <em>I</em> use GraphQL?</h3>
<p><em>I</em> certainly do not use GraphQL because it -</p>
<ul>
<li>is the in-thing in web development today</li>
<li>is revered by everyone who say there is nothing more wonderful</li>
<li>is typed, and that makes it so magical</li>
<li>provides a really good looking IDE that makes it so easy to develop the queries</li>
<li>makes services so easy to code (all it needs is a gesture and all the little thingies arrange themselves in perfect harmony)</li>
<li>drastically reduces chatty network traffic and improves the speed to &lsquo;burn rubber&rsquo; levels even before ignition is turned on</li>
<li>uses in-built security rules that makes it so easy to provide role-based authorization</li>
<li>is loved equally by server and client tech stacks</li>
<li>specifies a good abstraction layer for databases</li>
<li>has subscriptions and those are so so amazing</li>
<li>is developed by Facebook. I happen to adore Zuckerberg</li>
</ul>
<p>Or, I may be using GraphQL for a couple of points mentioned above and that is embarrassing to admit (or downright stupid).</p>
<p>The primary reasons for me to use GraphQL are -</p>
<ul>
<li>it reduces work on the server-side (surprise, surprise!?). I do not have to write APIs for each and every request, or over-engineer APIs imagining all possible scenarios. Since most of my services are data-heavy, I particularly like the fact that I don&rsquo;t need to maintain a dozen end-points</li>
<li>development iterations are easier on client. I can make adjustments to client code to fetch more or less data</li>
<li>pass only the required data b/w client and server</li>
</ul>
<p>But, on the other hand -</p>
<ul>
<li>GraphQL makes it harder to code server-side services (that is just me, not the fault of GraphQL per se).</li>
<li>I also find it harder to control the queries that clients request while they snicker in the background (the developers of the said client, not client-users. Since I am the only developer in many of my applications, I snicker to myself about myself. I need help!).</li>
</ul>
<p>I find it easier to just enable standard Rest pattern to start with (scaffolded most of the time). Then, I start enabling GraphQL services for specific transactions and go from there.</p>
<h3 id="err-what-are-you-even-talking-about">Err.. what are you even talking about?</h3>
<p>Let&rsquo;s see a few examples.</p>
<p>If I need to get a specific contact, I would do the following transaction in REST -</p>
<pre tabindex="0"><code>GET https://api.com/contact
</code></pre><p>The response will be -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">contacts</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">planet</span><span class="o">:</span> <span class="s2">&#34;earth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Rama&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">age</span><span class="o">:</span> <span class="mi">42</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">planet</span><span class="o">:</span> <span class="s2">&#34;mercury&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Kris&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">age</span><span class="o">:</span> <span class="mi">21</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">planet</span><span class="o">:</span> <span class="s2">&#34;mars&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Joe&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">age</span><span class="o">:</span> <span class="mi">63</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I can do the following in GraphQL and send it as a POST request to server -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">query</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nc">contacts</span><span class="p">(</span><span class="py">name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Rama&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nc">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">age</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The response will be like so -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">contacts</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Rama&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">age</span><span class="o">:</span> <span class="mi">42</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The responses in the two transactions speak for themselves!</p>
<h3 id="what-are-graphql-operations-and-why-should-i-care">What are GraphQL operations? And, why should I care?</h3>
<p>No matter what we do in the client-server world - we are trying to do one simple thing -</p>
<blockquote>
<p>Share data and business logic b/w server and client</p>
</blockquote>
<p>One can query, insert, update or delete data and can initiate those transactions from client. This can either happen -</p>
<ul>
<li>with the server not caring about the &ldquo;state&rdquo; of the client. i.e., client sends request (with authentication incl. as part of request if required), and server answers the call</li>
<li>server keeps client interests in mind, has an open channel with the client, and keeps track of data so that clients get changes as they happen</li>
</ul>
<p>GraphQL builds a layer on top of a database to assist you to transmit data in both cases.</p>
<h5 id="query">Query</h5>
<p>GraphQL queries help you to fetch only the records and attributes you need - we have seen examples earlier.</p>
<p>You can also include different entities (think &rsquo;tables&rsquo; as an analogy) in the request. Use GraphQL to fetch records with specified relationships, rather than making separate requests for distinct entities.</p>
<h5 id="mutation">Mutation</h5>
<p>GraphQL mutations are used to insert/change data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">mutation</span><span class="w"> </span><span class="nc">createContact</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">addContact</span><span class="p">(</span><span class="py">name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Joe&#34;</span><span class="p">,</span><span class="w"> </span><span class="nc">age</span><span class="p">:</span><span class="w"> </span><span class="nc">100</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The above query provides instruction to use a mutation called <code>createContact</code> that in turn uses <code>addContact</code> to add a contact record with name <code>Joe</code> and age <code>100</code>.</p>
<h5 id="subscription">Subscription</h5>
<p>Subscriptions establish a channel between server and client. Clients can subscribe to record updates and get notified (through a simple notification, or by actual &lsquo;push&rsquo; of the data from server) whenever there are changes.</p>
<p>Subscriptions use &lsquo;web sockets&rsquo; under the hood. This is a battle-tested piece of technology that enables real-time data updates to client.</p>
<p>For example - clients can use the below subscription to get the list of contacts who won the lottery.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">subscription</span><span class="w"> </span><span class="nc">listenWonLottery</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">wonLottery</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">deals</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Let&rsquo;s see the flow -</p>
<ol>
<li>Clients subscribe to contacts</li>
<li>One or more of the contacts get updated in the database when they win lottery</li>
<li>On database update, a notification gets triggered to clients</li>
<li>The notification contains contact&rsquo;s name and deals associated with contact</li>
<li>Client can thereon show a popup with contact name and show flying balloons for effect</li>
</ol>
<h3 id="what-are-the-different-components-required-to-implement-graphql">What are the different components required to implement GraphQL?</h3>
<p>GraphQL is just the language for API and runtime for executing them queries. It is not hardwired to database and does not specify how the database operations need to be executed.</p>
<p>The different components that implement GraphQL stack are below. These components are not part of GraphQL specifications but are just part of the stack that support transactions using GraphQL.</p>
<h5 id="server">Server</h5>
<p>The implementation/tech stack decides how GraphQL is incorporated in the transaction. Server receives GraphQL requests from clients, establishes connection to database, converts graph queries to a language understood by database, fetches results and sends the responses back to clients.</p>
<p>A server may also include the functions expected from a &ldquo;normal&rdquo; server -</p>
<ul>
<li>apply business logic to data</li>
<li>listen to supported types of requests on specified network ports</li>
<li>takes care of all the supporting infrastructure required to serve requests to clients.</li>
</ul>
<h5 id="database">Database</h5>
<p>Stores data and maintains atomicity, consistency etc. etc. for data. It is a glorified file system that supports individual transactions and does not mess up the data just because the network went kaput, or your client decided to play a game.</p>
<h5 id="client">Client</h5>
<p>Can be a browser, mobile app or anything that requests data from server and consumes server response to do &ldquo;something&rdquo;. We create GraphQL queries, mutations etc. and send them over in a simple POST request.</p>
<h3 id="what-are-the-terms-that-i-should-be-familiar-about">What are the terms that I should be familiar about?</h3>
<h5 id="schema">Schema</h5>
<p>A schema is the building block of GraphQL. It is described using a language that we can understand - it can be any language, but a simple option aptly named &ldquo;Schema Definition Language&rdquo; is used in the standard implementation.</p>
<p>Consider the below example -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nc">Contact</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">name</span><span class="p">:</span><span class="w"> </span><span class="nc">String</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">deal</span><span class="p">:</span><span class="w"> </span><span class="nc">Deal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nc">Deal</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">description</span><span class="p">:</span><span class="w"> </span><span class="nc">String</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">value</span><span class="p">:</span><span class="w"> </span><span class="nc">String</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The schema definition helps a client-side developer to see and understand the relationships b/w data entities, and thereon construct a GraphQL query.</p>
<h5 id="resolvers">Resolvers</h5>
<p>A resolver is a function that returns data in the shape specified by schema.</p>
<p>For e.g. -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">resolvers</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Query</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">contact</span><span class="p">(</span><span class="nx">root</span><span class="p">,</span> <span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">contacts</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">contact</span> <span class="p">=&gt;</span> <span class="nx">contact</span><span class="p">.</span><span class="nx">id</span> <span class="o">===</span> <span class="nx">args</span><span class="p">.</span><span class="nx">id</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">deals</span><span class="p">(</span><span class="nx">root</span><span class="p">,</span> <span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">deals</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">deal</span> <span class="p">=&gt;</span> <span class="nx">deal</span><span class="p">.</span><span class="nx">id</span> <span class="o">===</span> <span class="nx">args</span><span class="p">.</span><span class="nx">id</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Contact</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">deals</span><span class="o">:</span> <span class="nx">contact</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">deals</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">deal</span> <span class="p">=&gt;</span> <span class="nx">deal</span><span class="p">.</span><span class="nx">contactId</span> <span class="o">===</span> <span class="nx">contact</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>We use a resolver to write queries, mutations and subscriptions.</p>
<h5 id="object-type--fields">Object type / fields</h5>
<p>In the GraphQL query outlined above -</p>
<ul>
<li>Object type is &lsquo;Contact&rsquo; and &lsquo;Deal&rsquo;</li>
<li>Fields are <code>name</code>, <code>description</code>, and <code>value</code></li>
</ul>
<h3 id="this-is-so-cool-that-i-slept-through-the-article-give-it-to-me-straight---should-i-use-graphql">This is so cool that I slept through the article. Give it to me straight - should I use GraphQL?</h3>
<p>Not everywhere.</p>
<p>Use GraphQL when you need to fetch a lot of data having varying shapes / many relationships. Use GraphQL to simplify the data passed between client and server.</p>
<p>At the same time -</p>
<ol>
<li>Remember that plain Rest API implementations are simpler</li>
<li>Even in data-heavy applications, I do not see a big advantage of applying GraphQL everywhere (except architectural uniformity/ standardization, maybe). For e.g. if you have narrow relationships between entities that do not require related data views all the time</li>
<li>Keep track of how clients use GraphQL - crazy queries can lead to performance issues</li>
</ol>
<p>Why do I find Rest services simpler?</p>
<ol>
<li>Endpoint setup is simple on the server. Expose an API or two, and use an ORM to fetch/update data in one or two steps - no sight of fields and related entities anywhere unless you want to deal with them</li>
<li>In addition to the previous point, or supplementing it, is the fact that the tooling is amazing - the technology is so old and supported so well that most of the time it just requires a couple of clicks (or lines of code) to fully set up</li>
<li>I control queries in the back-end - easier control on who accesses the data and how. I also find it easier to solve potential performance issues since I control filters and sort on the server</li>
<li>But all said and done, I may be finding Rest easier since I have spent so much time with it :)</li>
</ol>
<h3 id="ok-i-am-in-where-should-i-start">Ok, I am in. Where should I start?</h3>
<p>I find the following technology stacks easy to follow and useful to learn for the long run. The recommendations are skewed towards Vue since I use Vue all the time and strongly believe that Vue is easier on beginners.</p>
<h5 id="enable-graphql-server-side">Enable GraphQL server-side</h5>
<p>It is a good idea to create a server on your own and see GraphQL flow in the back-end for real.</p>
<table>
  <thead>
      <tr>
          <th>Category</th>
          <th>Recommended</th>
          <th>Remarks</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>I know SQL and understand DBs</td>
          <td><a href="https://www.graphile.org/postgraphile/">PostGraphile</a></td>
          <td>Refer <a href="/spin-up-your-own-local-graphql-server-within-15-min/">this</a></td>
      </tr>
      <tr>
          <td>I want to learn more of serverless</td>
          <td><a href="https://aws.amazon.com/appsync/">AppSync</a></td>
          <td>Sign up for 1-year limited free servers on Amazon AppSync</td>
      </tr>
      <tr>
          <td>I want to learn GraphQL using a CMS</td>
          <td><a href="https://strapi.io/">StrapiJS</a></td>
          <td>In beta, but good for production</td>
      </tr>
      <tr>
          <td>I want to experiment with static sites + GraphQL</td>
          <td><a href="https://gridsome.org/">Gridsome</a></td>
          <td>New, but promising.</td>
      </tr>
      <tr>
          <td>I know PHP</td>
          <td>Laravel + <a href="https://lighthouse-php.com/">Lighthouse</a></td>
          <td></td>
      </tr>
      <tr>
          <td>I am looking forward to a long-term relationship</td>
          <td><a href="https://nestjs.com/">NestJS</a></td>
          <td>Good server capabilities + good GraphQL support</td>
      </tr>
      <tr>
          <td>I am a master</td>
          <td>Guide me towards enlightenment, maybe</td>
          <td>What are you even doing here?</td>
      </tr>
  </tbody>
</table>
<h5 id="use-graphql-on-the-client">Use GraphQL on the client</h5>
<ul>
<li><a href="https://vuejs.org">Vue</a> + <a href="https://github.com/Akryum/vue-apollo">Vue-Apollo</a></li>
<li><a href="https://svelte.dev/">Svelte</a> + <a href="https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost">Apollo</a>.</li>
</ul>
<p>Also see: <a href="/create-a-simple-to-do-app-using-svelte-and-graphql/">create a simple to-do with Svelte + GraphQL</a></p>
<h3 id="finis">Finis</h3>
<p>It ends here :)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How not to design Rest services</title>
      <link>https://techformist.com/not-designing-rest-services/</link>
      <pubDate>Tue, 20 Aug 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/not-designing-rest-services/</guid>
      <description>&lt;p&gt;How not to design Rest services?&lt;/p&gt;
&lt;p&gt;This is not a theoritical introduction to Rest - there&amp;rsquo;s something called the &amp;lsquo;Great Internet&amp;rsquo; for that. But, what I do want to (cautiously) write about is how I mess up the design principles carefully laid out by smarter people.&lt;/p&gt;
&lt;h3 id=&#34;practice-1-use-verbs-in-resource-names&#34;&gt;Practice 1: Use verbs in resource names&lt;/h3&gt;
&lt;p&gt;We typically use the following API names -&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET https://api.com/contact
PATCH https://api.com/contact
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is not a problem in the normal course of designing services. But, when it is time to get some complex queries - I always do a rain dance and come up with stupid names.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>How not to design Rest services?</p>
<p>This is not a theoritical introduction to Rest - there&rsquo;s something called the &lsquo;Great Internet&rsquo; for that. But, what I do want to (cautiously) write about is how I mess up the design principles carefully laid out by smarter people.</p>
<h3 id="practice-1-use-verbs-in-resource-names">Practice 1: Use verbs in resource names</h3>
<p>We typically use the following API names -</p>
<pre tabindex="0"><code>GET https://api.com/contact
PATCH https://api.com/contact
</code></pre><p>This is not a problem in the normal course of designing services. But, when it is time to get some complex queries - I always do a rain dance and come up with stupid names.</p>
<pre tabindex="0"><code>GET https://api.com/getAccountSalesActivitySummaryForMonth
</code></pre><p><strong>Best practice</strong></p>
<ul>
<li>do not use verbs, use nouns</li>
<li>represent the resource in the name (if there is more than one resource, pick your favourite)</li>
<li>use simple URLs</li>
</ul>
<h3 id="practice-2-patterns-that-no-one-understands">Practice 2: Patterns that no one understands</h3>
<p>APIs like below are partly descriptive but make it difficult to maintain consistency in naming conventions.</p>
<pre tabindex="0"><code>GET https://api.com/getAccountSalesActivitySummaryForMonth
POST https://api.com/createMaintenanceActivitiesForQuarter
PUT https://api.com/updateAssignmentForDay
</code></pre><p><strong>Best practice</strong></p>
<ul>
<li>already covered in the previous best practice ( I had a brain freeze while starting practice 2 )</li>
</ul>
<h3 id="practice-3-use-param-in-place-of-a-proper-uri">Practice 3: Use param in place of a proper URI</h3>
<p>If I need a specific activity for a contact -</p>
<pre tabindex="0"><code>GET https://api.com/activities/?q={contactId=&#39;1-ABC&#39;}
</code></pre><p>Why do this? Well, I can reuse services on the server side and write same services to -</p>
<ul>
<li>bring in the activities for the particular contact (or)</li>
<li>for any of the thousand queries that a client throws at me.</li>
</ul>
<p>The practice of using ids as queries is not &ldquo;standard&rdquo; and not clearly understood by everyone. I live in constant fear of the maintenance guy ringing my doorbell.</p>
<p><strong>Best practice</strong></p>
<p>The following is most often seen to fetch related records.</p>
<pre tabindex="0"><code>GET https://api.com/contact/1-ABC/activities
</code></pre><h3 id="practice-4-use-patch-and-put-interchangeably">Practice 4: Use PATCH and PUT interchangeably</h3>
<p>I tend to use -</p>
<ol>
<li>Use PATCH if you are incrementally updating the record</li>
<li>Use PUT when you want the new record to completely update the previous record - lock, stock and without id (may be?)</li>
</ol>
<p>But, I have not strictly adhered to the said practice.</p>
<p>There are many who disagree with the PUT/PATCH argument and have been waging a war about where to use what.</p>
<p><strong>Best practice</strong></p>
<p>Follow the above method or otherwise. But, just follow ONE way of doing things.</p>
<p>If you are working in ASP.NET that scaffolds PUT even for a PATCH - get prepared for end of the world. Change the scaffolding or change the tech stack.</p>
<h3 id="practice-5-create-distinct-apis-for-role-based-filters">Practice 5: Create distinct APIs for role-based filters</h3>
<p>I don&rsquo;t quite know when I started this bizarre practice.</p>
<p>Let say I have to filter data to address the below requirements -</p>
<ol>
<li>A rep should see her records</li>
<li>A manager should see her records and her reps&rsquo; records</li>
<li>A CEO should see no records until the company gets bankrupt</li>
</ol>
<p>I create three APIs -</p>
<pre tabindex="0"><code>GET https://api.com/my-deal/
GET https://api.com/mgr-deal/
GET https://api.com/no-deal/
</code></pre><p>Internally these fetch data from the same service (<code>getDealsForRole</code>), but only after setting the appropriate filters.</p>
<p>Why do that? I find it easier to manage security and filters in controller/services rather than routes. The resources also are self-contained to manage errors, and can apply complex filter criteria to client-calls or calls from other services (e.g. manager should see reps&rsquo; deals but not her private activities).</p>
<p>The problem of applying role-based filters is easily done by using a standard ACL or even by introducing filters, but not all tech stacks have standard ACLs and the damage has already been done for quite a few applications.</p>
<p><strong>Best practice</strong></p>
<ul>
<li>Use standard ACLs - if your tech stack supports it. Else, pray and use below alternatives.</li>
<li>Apply security at the gate and this is at the route-level.</li>
<li>You could also develop ACL such that the filters get applied without service explicitly requesting for it</li>
</ul>
<h3 id="practice-6-stuff-response-with-everything-you-may-not-get-a-second-chance">Practice 6: Stuff response with everything, you may not get a second chance</h3>
<p>My typical service gets to serve related records along with the main entity.</p>
<p>For e.g. -</p>
<pre tabindex="0"><code>GET https://api.com/account
</code></pre><p>The account request may be stuffed to fetch account, account&rsquo;s contacts and deals.</p>
<p>Normally I would avoid fetching all the data that the client did not request for, but I just hate to do too many requests to server. Without all the data, the client has to fetch -</p>
<ul>
<li>list of records</li>
<li>optionally the detail record at some point of time</li>
<li>related records</li>
</ul>
<p>Rather than see the streamlined &ldquo;fetch&rdquo;, I include contacts and deals along with the account request. And, before becoming fully aware of what I did - I start throwing queries at database that use some weird joins to fetch all kinds of records everywhere.</p>
<ul>
<li>Close coupling of client-server is a problem if you have to serve multiple types of clients. Should never be used for public services</li>
<li>will reduce traffic but increases payload</li>
<li>sooner of later I will design a &ldquo;simpler&rdquo; service since the main service took so long to fetch results (and the madness continues)</li>
</ul>
<p><strong>Best practice</strong></p>
<p>Keep your services contained and don&rsquo;t let them go crazy.</p>
<p>There is nothing wrong in firing a query to get details (they are separate components anyway).</p>
<h3 id="finis">Finis</h3>
<p>I will stop here.</p>
<p>Not because I don&rsquo;t know what other mistakes I make. It is just that I realized how little I know after going through this post more than once.</p>
<p>It is probably time for me to hit that refresh button. I am switching to RPC y&rsquo;all.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hosting Models for Blazor</title>
      <link>https://techformist.com/hosting-models-blazor/</link>
      <pubDate>Tue, 13 Aug 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/hosting-models-blazor/</guid>
      <description>&lt;p&gt;Blazor has three hosting models - as I see it :)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server&lt;/li&gt;
&lt;li&gt;Client&lt;/li&gt;
&lt;li&gt;Client++&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will look at them in brief below.&lt;/p&gt;
&lt;h3 id=&#34;server&#34;&gt;Server&lt;/h3&gt;
&lt;p&gt;Server hosted implies the entire application being hosted on server. A razor-thin (no pun) app is delivered to client and there on client relies on server for everything.&lt;/p&gt;
&lt;p&gt;I mean everything -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All clicks, user input, gestures etc. will be transferred to server through a SignalR connection. Server figures out what to do&lt;/li&gt;
&lt;li&gt;Server does DOM diffing, keeps track of updates that are done client-side and what should be sent to client. Server delivers those changes to client over the ever-persistent SignalR&lt;/li&gt;
&lt;li&gt;Server sees any connection drops and maintains state if client reconnects&lt;/li&gt;
&lt;li&gt;Client reacts, and keeps reacting with help from server&lt;/li&gt;
&lt;li&gt;Can work on older browsers (though, to be frank, if you are using old browsers - you should be upgrading old browsers and the apps rather than reading this article)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Though this may sound primitive, but works wonderfully well - provided you have good connectivity to server and a fast-enough server.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Blazor has three hosting models - as I see it :)</p>
<ul>
<li>Server</li>
<li>Client</li>
<li>Client++</li>
</ul>
<p>We will look at them in brief below.</p>
<h3 id="server">Server</h3>
<p>Server hosted implies the entire application being hosted on server. A razor-thin (no pun) app is delivered to client and there on client relies on server for everything.</p>
<p>I mean everything -</p>
<ul>
<li>All clicks, user input, gestures etc. will be transferred to server through a SignalR connection. Server figures out what to do</li>
<li>Server does DOM diffing, keeps track of updates that are done client-side and what should be sent to client. Server delivers those changes to client over the ever-persistent SignalR</li>
<li>Server sees any connection drops and maintains state if client reconnects</li>
<li>Client reacts, and keeps reacting with help from server</li>
<li>Can work on older browsers (though, to be frank, if you are using old browsers - you should be upgrading old browsers and the apps rather than reading this article)</li>
</ul>
<p>Though this may sound primitive, but works wonderfully well - provided you have good connectivity to server and a fast-enough server.</p>
<ul>
<li>We have no business logic going to the client - server takes care of everything</li>
<li>Infrastructure is under tight control and you can do whatever you please (well, it&rsquo;s <em>our</em> server)</li>
</ul>
<p>At the same time -</p>
<ul>
<li>There will be delays. It is better to do processing locally than on the server. It will be interesting to see real-world performance, but I do not expect this to be fast</li>
<li>Everything is dependent on server - so strictly online feature set</li>
<li>Client-side connections and all that work will put significant stress on servers (I was not a fan of such <a href="/universal-apps-meteorjs-2019/">client connections in MeteorJS</a>, I really doubt server-side Blazor will change my mind)</li>
</ul>
<p>I think server-hosted models will be a boon to a whole lot of stupid enterprise applications that have been begging for an upgrade since Y2000. I see server-hosted Blazor as the new client-server - only that we now follow universal standards :).</p>
<p>Server-side hosting is also easier to control and implement - Microsoft plans to deliver these as part of the .NET Core v3 full release later this year. The only positive I see here is the web-socket like SignalR. We do not need to write a lot of boiler-plate to control those server-client interactions but let the underlying framework handle them.</p>
<h3 id="client">Client</h3>
<p>Client-side hosting falls to the other extreme. The app delivers itself to the client and runs as a full-fledged .NET application using Mono within the browser.</p>
<p>Once the app DLLs (yes, DLLs) and WASM running infrastructure is downloaded to client, the client does not need the server anymore.</p>
<ul>
<li>All processing happens on the client side (except online connections and updates to server DB, of course)</li>
<li>UI generation, reacting to changes and events happen on the client side</li>
</ul>
<p>I could also deliver this blog entirely to your desktop - take that, Blogspot.</p>
<p>On the flip side -</p>
<ul>
<li>There is much more dependence on browser to stick to WASM standards and get everything done</li>
<li>Initial download size is larger. It is not so large to cause a commotion - we see MBs getting downloaded from style libraries even today</li>
<li>WASM is comparatively young technology, and may not be the best choice today for applications with stability as a requirement</li>
</ul>
<h3 id="the--sub-division-and-selecting-a-hosting-model">The <code>++</code> Sub-division and Selecting a Hosting Model</h3>
<p>When you click on <code>New Project</code> in Visual Studio, and select <code>Blazor</code> template, you will see the exact two options outlined above.</p>
<p><img loading="lazy" src="/misc/blazor-template-hosting-model.jpg" type="" alt=""  /></p>
<p>In addition to the options, you will also see a checkbox called <code>ASP.NET Core Hosted</code> for <code>Blazor WebAssembly App</code>. This is the result of a simplified model in Preview 8, and which I have come to appreciate.</p>
<p>By selecting the box, you are instructing the the scaffolding to create -</p>
<ol>
<li>Client-side code</li>
<li>Server-side code that can enable your app to have server-side logic including those done by services/controllers in a MVC model</li>
<li>Shared code, which can be shared by client and server</li>
</ol>
<p>Any web app is not an island by itself. Using a core hosted model with WASM, we can create applications that do most of the work on the client-side, but can also fall back on server for sensitive/proprietary business logic, maintaining centralized data store, and other such use cases.</p>
<p>I am personally excited about the server-hosted WASM. That is the future, folks - does not matter whether you mark or unmark my words.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Format Hugo markdown and code</title>
      <link>https://techformist.com/format-hugo-markdown-code/</link>
      <pubDate>Fri, 09 Aug 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/format-hugo-markdown-code/</guid>
      <description>&lt;p&gt;Format your markdown using &lt;code&gt;prettier&lt;/code&gt; and you are off to the races.&lt;/p&gt;
&lt;p&gt;Hugo static site generator keeps everything simple. I just love the power of typing in something in markdown, and seeing the finished HTML pages and blog formatted to specs (in no time, I might add).&lt;/p&gt;
&lt;p&gt;I use VSCode for writing markdown text. This works out beautifully - I can save markdown files and prettier kicks in to format everything - the markdown within those files and the code within markdown.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Format your markdown using <code>prettier</code> and you are off to the races.</p>
<p>Hugo static site generator keeps everything simple. I just love the power of typing in something in markdown, and seeing the finished HTML pages and blog formatted to specs (in no time, I might add).</p>
<p>I use VSCode for writing markdown text. This works out beautifully - I can save markdown files and prettier kicks in to format everything - the markdown within those files and the code within markdown.</p>
<h3 id="why-use-vscode">Why use VSCode?</h3>
<p>I keep markdown and code typing similar - no brain-freeze. And, the formatting is a huge bonus as you see below.</p>
<p>What I type -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gu">###   Header 3
</span></span></span><span class="line"><span class="cl"> Hello World
</span></span><span class="line"><span class="cl">``<span class="sb">`js
</span></span></span><span class="line"><span class="cl"><span class="sb">  if (true){
</span></span></span><span class="line"><span class="cl"><span class="sb">console.log(&#34;hello world&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">}
</span></span></span><span class="line"><span class="cl"><span class="sb">  `</span>``
</span></span></code></pre></div><p>What the content gets automatically formatted to -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gu">### Header 3
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Hello World
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="s">```js
</span></span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;hello world&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="s">```</span>
</span></span></code></pre></div><p>You may see that -</p>
<ul>
<li><code>###</code> line has extra spaces removed</li>
<li>a blank line has been added between header and content</li>
<li>another blank line added between content and the beginning of code block</li>
<li>the <code>js</code> block has been changed to -
<ul>
<li>format <code>if</code> statement as a code-block</li>
<li>semicolons at logical end</li>
</ul>
</li>
</ul>
<p>If you have been typing in markdown, you would have recognized how valuable adding blank line can be. While it is easier for prettier, markdown does not &ldquo;see&rdquo; what you meant and format the entire block of content in an altogether different way.</p>
<p>Another crucial advantage for me - I can type in code-blocks within markdown and let it automatically format. I also get to know any silly mistakes (e.g. a bracket not closed) since Prettier parser will not format the code.</p>
<h3 id="the-alternative">The Alternative</h3>
<p>If VSCode is working so well with a couple of extensions, why change that? Two reasons -</p>
<ol>
<li>I had been trying to make my workflow even more seamless</li>
<li>VSCode gobbles up a lot of memory. I want to minimise stuff I do on VSCode other than main-stream programming. Obviously all those windows are going to be open all the time, which is a big problem</li>
</ol>
<p>I wanted to retain VSCode&rsquo;s fantastic formatting support, and I can do that in just a couple of steps - thanks to Hugo.</p>
<ol>
<li>Go to your Hugo site root directory</li>
<li>If your theme does not use any kind of packages today - do <code>npm init</code>. (in other words, ignore this step if you already have <code>package.json</code> in your Hugo site root directory)</li>
<li>Install husky, lint-staged, and prettier packages -
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">  npm install husky lint-staged prettier
</span></span></code></pre></div></li>
<li>Change your <code>package.json</code> to-
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;precommit&#34;</span><span class="p">:</span> <span class="s2">&#34;lint-staged&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;lint-staged&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;*.md&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;prettier --parser markdown --write&#34;</span><span class="p">,</span> <span class="s2">&#34;git add&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<p>We parse markdown file with <code>lint-staged</code> and use <code>prettier</code> to format files. <code>husky</code> enables us to prevent accidental commits before running pre-commit hook.</p>
<p>That&rsquo;s it - now you can see your files nicely formatted no matter which editor you use.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Blazor for Production Anyone? Preview 7 is Released</title>
      <link>https://techformist.com/blazor-for-production-preview7/</link>
      <pubDate>Wed, 31 Jul 2019 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/blazor-for-production-preview7/</guid>
      <description>&lt;p&gt;Blazor is a big part of why I am looking forward to .NET 3.0. That&amp;rsquo;s one of the reasons I try to play around with the individual releases.&lt;/p&gt;
&lt;p&gt;Though Release 6 (I think ) caused some pain, the release schedules seem to be good as previews. As I understand - the ASP.NET Core team had been working on ironing out issues and making the application ready. That has come to a larger milestone since &lt;a href=&#34;https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-7/&#34;&gt;Release 7 has been out for a week&lt;/a&gt; and it is said to be ready for production.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Blazor is a big part of why I am looking forward to .NET 3.0. That&rsquo;s one of the reasons I try to play around with the individual releases.</p>
<p>Though Release 6 (I think ) caused some pain, the release schedules seem to be good as previews. As I understand - the ASP.NET Core team had been working on ironing out issues and making the application ready. That has come to a larger milestone since <a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-7/">Release 7 has been out for a week</a> and it is said to be ready for production.</p>
<p>I created multiple versions of a standard &rsquo;to do&rsquo; app through the versions using both client and server-side Blazor - loving the development so far. Though Materialize CSS is a good option, I still do not believe that is not at the same level as Bootstrap within the .NET world. Again, that may be how I see the world and that can be totally wrong.</p>
<p>Documentation was particularly important for a poor .NET developer like myself - that seems to have been improving over at the <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started?view=aspnetcore-3.0&amp;tabs=visual-studio">ASP.NET Core Preview docs site</a>.</p>
<p>I see a few of the older channels publishing videos on Blazor, but .NET does not seem to be as happening a place when it comes to community. Or, I may have been spoilt by choice and the over-enthusiastic Javascript community.</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=uW-Kk7Qpv5U">NDC Conference - July</a></li>
<li>Microsoft published videos <a href="https://www.youtube.com/watch?v=PiJtEZYMxOc">Nov 18</a></li>
<li><a href="https://www.youtube.com/watch?v=CaxR4_fP-FA">Blazor tutorial video by Tim Corey</a> [updated]</li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
