<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alvin Lim</title>
    <description>The latest articles on DEV Community by Alvin Lim (@alvinqingxing).</description>
    <link>https://dev.to/alvinqingxing</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F391479%2F39f57cb7-3751-4fbb-be0a-ad42862b17c1.jpg</url>
      <title>DEV Community: Alvin Lim</title>
      <link>https://dev.to/alvinqingxing</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alvinqingxing"/>
    <language>en</language>
    <item>
      <title>HTTPS Domain Forwarding for Free via Netlify</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Tue, 02 Nov 2021 14:39:09 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-netlify-2dha</link>
      <guid>https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-netlify-2dha</guid>
      <description>&lt;p&gt;This evening GoDaddy reminded me of an unused personal domain, &lt;code&gt;alvinlim-writer.com&lt;/code&gt;, which is due to expire late next year. Several years ago I had used it for my personal website, and it still appears as a search result on Google. As such I decided to forward this domain to my current website &lt;a href="https://alvinlim.dev"&gt;alvinlim.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had previously done this with another domain via &lt;a href="https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-github-pages-bf6"&gt;GitHub Pages&lt;/a&gt;, but this time I decided to go with Netlify as I suspected doing so would be easier.&lt;/p&gt;

&lt;p&gt;So first I created a bare bones &lt;code&gt;index.html&lt;/code&gt; file containing a meta refresh tag that would load my target URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta http-equiv="refresh" content="0; URL=https://alvinlim.dev"&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in Netlify I deployed this &lt;code&gt;index.html&lt;/code&gt; file as a running website. Usually I deploy projects to Netlify by connecting to the appropriate repo on GitHub, but for this I just clicked the &lt;code&gt;Sites&lt;/code&gt; tab and dragged and dropped the folder containing the &lt;code&gt;index.html&lt;/code&gt; file into the following button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WR8CpI6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opojtof0c9ceftkaieoq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WR8CpI6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opojtof0c9ceftkaieoq.png" alt="Folder upload button on Netlify" width="550" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;index.html&lt;/code&gt; file was deployed as an active website -- just a few seconds later -- Netlify gave me the option to set up a custom domain for this website. This was my opportunity to connect my old domain &lt;code&gt;alvinlim-writer.com&lt;/code&gt; to this &lt;code&gt;index.html&lt;/code&gt; file which would automatically redirect the user to my current website &lt;a href="https://alvinlim.dev"&gt;alvinlim.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To set up this custom domain, Netlify gave me the A and CNAME records that I would need to set up in my domain name registrar, in this case, GoDaddy. In GoDaddy, I set up the Netlify A and CNAME records in my &lt;code&gt;alvinlim-writer.com&lt;/code&gt; management panel, and almost immediately the URL pointed to the website I had set up in Netlify! Which meant typing &lt;a href="https://alvinlim-writer.com"&gt;alvinlim-writer.com&lt;/a&gt; in the browser would automatically redirect you to &lt;a href="https://alvinlim.dev"&gt;alvinlim.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From my recollection, setting this up on Netlify was much easier than doing the same on GitHub Pages. Having said that, setting up HTTPS domain forwarding on &lt;a href="https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-github-pages-bf6"&gt;GitHub Pages&lt;/a&gt; can't be described as a difficult task either!&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>HTTPS Domain Forwarding for Free via GitHub Pages</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Fri, 29 Jan 2021 08:56:17 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-github-pages-bf6</link>
      <guid>https://dev.to/alvinqingxing/https-domain-forwarding-for-free-via-github-pages-bf6</guid>
      <description>&lt;p&gt;This week I decided to move my homepage from &lt;a href="https://alvinlim.me"&gt;&lt;code&gt;alvinlim.me&lt;/code&gt;&lt;/a&gt; to &lt;a href="https://alvinlim.dev"&gt;&lt;code&gt;alvinlim.dev&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I remembered that my domain name registrar offered a free &lt;a href="https://www.namecheap.com/support/knowledgebase/article.aspx/384/2237/what-is-url-redirect-and-how-does-it-work/"&gt;URL redirect&lt;/a&gt; service, and I decided to use that forward &lt;code&gt;alvinlim.me&lt;/code&gt; to &lt;code&gt;alvinlim.dev&lt;/code&gt;. While that worked for &lt;code&gt;http://alvinlim.me&lt;/code&gt;, clicking &lt;code&gt;https://alvinlim.me&lt;/code&gt; instead threw an SSL error.&lt;/p&gt;

&lt;p&gt;Basically, for the URL redirect of &lt;code&gt;https://alvinlim.me&lt;/code&gt; to work, &lt;code&gt;https://alvinlim.me&lt;/code&gt; first had to be recognized by the browser as a valid website, and this meant it had to have a valid SSL certificate attached to it. &lt;/p&gt;

&lt;p&gt;This meant I had to set up a website for &lt;code&gt;https://alvinlim.me&lt;/code&gt;, attach a SSL certificate to it, and configure the HTML to redirect the browser to &lt;code&gt;https://alvinlim.dev&lt;/code&gt;. 😅&lt;/p&gt;

&lt;p&gt;This raised the question -- wouldn't setting up the website and SSL certificate be expensive? That's when I remembered that I could quickly set up a free website on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;, connect it to my &lt;code&gt;alvinlim.me&lt;/code&gt; domain, and GitHub Pages would even provision and attach an SSL certificate to it for free! Since I already had the &lt;code&gt;alvinlim.me&lt;/code&gt; domain, I decided to go with this option.&lt;/p&gt;

&lt;p&gt;So I created a &lt;a href="https://github.com/alvinqingxing/homepage-redirect"&gt;new repo on GitHub&lt;/a&gt; which hosts an &lt;code&gt;index.html&lt;/code&gt; file which is empty except for the important &lt;a href="https://www.w3.org/TR/WCAG20-TECHS/H76.html"&gt;meta refresh tag&lt;/a&gt; which redirects the browser to the new URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta http-equiv="refresh" content="0; URL=https://alvinlim.dev"&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I next &lt;a href="https://docs.github.com/en/github/working-with-github-pages/creating-a-github-pages-site"&gt;created a GitHub Pages site&lt;/a&gt; from that repo and &lt;a href="https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site"&gt;attached my domain&lt;/a&gt; to it. In the GitHub Pages settings I then selected the &lt;a href="https://docs.github.com/en/github/working-with-github-pages/securing-your-github-pages-site-with-https"&gt;"Enforce HTTPS" option&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With all this in place, clicking &lt;a href="https://alvinlim.me"&gt;&lt;code&gt;https://alvinlim.me&lt;/code&gt;&lt;/a&gt; no longer throws an SSL error but instead correctly redirects the browser to &lt;a href="https://alvinlim.dev"&gt;&lt;code&gt;https://alvinlim.dev&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to make Windows Terminal look like oh-my-zsh</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Thu, 12 Nov 2020 11:55:17 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/how-to-make-windows-terminal-look-like-oh-my-zsh-1efh</link>
      <guid>https://dev.to/alvinqingxing/how-to-make-windows-terminal-look-like-oh-my-zsh-1efh</guid>
      <description>&lt;p&gt;One of the joys of working in a Linux environment is its superior terminal experience. With the wide selection of &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Themes" rel="noopener noreferrer"&gt;themes&lt;/a&gt; available in the &lt;a href="https://www.freecodecamp.org/news/jazz-up-your-zsh-terminal-in-seven-steps-a-visual-guide-e81a8fd59a38/" rel="noopener noreferrer"&gt;oh-my-zsh&lt;/a&gt; plugin for the zsh shell -- and likewise for &lt;a href="https://github.com/ohmybash/oh-my-bash/wiki/Themes" rel="noopener noreferrer"&gt;oh-my-bash&lt;/a&gt; for the bash shell, using the terminal in Linux can be a delightful aesthetic pleasure.&lt;/p&gt;

&lt;p&gt;Hence switching from a Linux to a Windows environment had always come with a sense of disappointment. So it was with great surprise when I learned that it was now possible to configure Windows Terminal to look like oh-my-zsh. &lt;a href="https://docs.microsoft.com/en-us/windows/terminal/tutorials/powerline-setup" rel="noopener noreferrer"&gt;The instructions can be found here&lt;/a&gt;. And if you implement these steps in &lt;a href="https://www.windowscentral.com/windows-terminal-15-hits-preview-loads-new-features" rel="noopener noreferrer"&gt;Windows Terminal 1.5&lt;/a&gt;, hyperlinks -- which previously had to be copied and pasted into the browser -- now become clickable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp7rjfsbr2zz89k8r3wa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp7rjfsbr2zz89k8r3wa4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/terminal/tutorials/powerline-setup" rel="noopener noreferrer"&gt;The instructions&lt;/a&gt; include the installation of a Powerline font like &lt;a href="https://github.com/microsoft/cascadia-code" rel="noopener noreferrer"&gt;Cascadia Code&lt;/a&gt;. The added benefit is that, once installed, you can &lt;a href="https://www.youtube.com/watch?v=qEdvWFWpOyQ" rel="noopener noreferrer"&gt;use the same font&lt;/a&gt; in your favorite code editor like Visual Studio Code, which can enhance your developer experience.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a vanilla JS navbar</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Sun, 11 Oct 2020 16:05:28 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/building-a-vanilla-js-navbar-36k4</link>
      <guid>https://dev.to/alvinqingxing/building-a-vanilla-js-navbar-36k4</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/alvinqingxing/less-can-be-more-a-lot-more-1a5i"&gt;last iteration&lt;/a&gt; of my &lt;a href="https://alvinlim.me/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt;, I removed all JavaScript and used HTML5's details/summary elements to selectively show or hide the different sections of my website.&lt;/p&gt;

&lt;p&gt;While this led my website to achieve a 100% Lighthouse performance score, I didn't like the resulting appearance. Hence I decided to bring back just enough JavaScript to build a simple navbar to accomplish that same task.&lt;/p&gt;

&lt;p&gt;This is how I wanted my website to look. When you first land on my website -- and if you click the "Hello" button in the navbar -- you will be presented with the "About" section:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffaqroxuyulpel8thoynz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffaqroxuyulpel8thoynz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click the "Code" button, you will be presented with the Coding Projects section of the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqvjpqkfivkndxecucegw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqvjpqkfivkndxecucegw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, if you click the "Contact" button, you will be presented with the contact form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8snfd1set1euzs5zj75s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8snfd1set1euzs5zj75s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key thing to note is that these three sections are not three separate HTML files but are instead three sections of one HTML file. JavaScript is used to hide two of these three sections at any one time, creating the illusion that there are three separate files. This can be seen when JavaScript is disabled. In this situation, all 3 sections are displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftmmvb5lvfq1sx9gn1qbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftmmvb5lvfq1sx9gn1qbz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve this look, I first identified the About, Coding Projects, and Contact Form sections with the appropriate id tags in the HTML: &lt;code&gt;&amp;lt;article id="about"&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;article id="code"&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;article id="contact"&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the navbar, each button -- which is just an anchor element with text and an icon from &lt;a href="http://fontastic.me/" rel="noopener noreferrer"&gt;Fontastic&lt;/a&gt; -- has its own unique id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;nav&amp;gt;
  &amp;lt;a id="nav-about"&amp;gt;#Hello &amp;lt;i class="icon-smile-o"&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;
  &amp;lt;a id="nav-code"&amp;gt;#Code &amp;lt;i class="icon-codepen"&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;
  &amp;lt;a id="nav-contact"&amp;gt;#Contact &amp;lt;i class="icon-pencil"&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once styled with CSS, the buttons wil look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcb4lzziyjxg9o3061vqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcb4lzziyjxg9o3061vqa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these ids in place, we can now use JavaScript to pair each button element in the navbar with its corresponding article element in the HTML, and create event listeners to change the appearance of the website when the different buttons are clicked.&lt;/p&gt;

&lt;p&gt;First, the navbar. Since the navbar uses JavaScript to control the appearance of the website, it is useless if JavaScript is -- for whatever reason -- disabled on the browser. In this situation, I do not want the navbar to appear. Hence in CSS the navbar is hidden by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nav {
  display: none;
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If JavaScript is working in the browser, the following script in the HTML will activate the navbar. The script will also display the "About" section and hide the "Code" and "Contact" sections, giving us the desired default appearance of the website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;script&amp;gt;
    document.querySelector("nav").style.display = "flex";
    document.getElementById("about").style.display = "block";
    document.getElementById("code").style.display = "none";
    document.getElementById("contact").style.display = "none";
    ...
  &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the event listeners can be added. When the "Hello" button (with the &lt;code&gt;nav-about&lt;/code&gt; id) is clicked, the "About" section should be shown while the "Code" and "Contact" sections remain hidden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;script&amp;gt;
    ...
    document.getElementById("nav-about").addEventListener("click", function () {
      document.getElementById("about").style.display = "block";
      document.getElementById("code").style.display = "none";
      document.getElementById("contact").style.display = "none";
    });
   ...
  &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the "Code" button (id: &lt;code&gt;nav-code&lt;/code&gt;) is clicked, the "Code" section should be shown and the "About" and "Contact" sections hidden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;script&amp;gt;
    ...
    document.getElementById("nav-code").addEventListener("click", function () {
      document.getElementById("about").style.display = "none";
      document.getElementById("code").style.display = "block";
      document.getElementById("contact").style.display = "none";
    });
    ...
  &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, when the "Contact" button (id: &lt;code&gt;nav-contact&lt;/code&gt;) is clicked, the "Contact" section should appear with the other two sections remaining hidden.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;script&amp;gt;
    ...
    document.getElementById("nav-contact").addEventListener("click", function () {
      document.getElementById("about").style.display = "none";
      document.getElementById("code").style.display = "none";
      document.getElementById("contact").style.display = "block";
    });
  &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! With these event listeners in place, the buttons are now functional. With the reintroduction of JavaScript to my website, I had concerns about the impact on performance, but a Lighthouse audit allayed my fears:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqhwntq8xvuwcrlkiayal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqhwntq8xvuwcrlkiayal.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested, the &lt;a href="https://github.com/alvinqingxing/homepage" rel="noopener noreferrer"&gt;source code&lt;/a&gt; of my website is available on Github.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Less can be More, A Lot More</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Tue, 22 Sep 2020 07:28:37 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/less-can-be-more-a-lot-more-1a5i</link>
      <guid>https://dev.to/alvinqingxing/less-can-be-more-a-lot-more-1a5i</guid>
      <description>&lt;p&gt;After I posted my last article, &lt;a href="https://dev.to/alvinqingxing/speeding-up-my-website-1fdl"&gt;Speeding up my website&lt;/a&gt;, I wanted to see if I could further optimize &lt;a href="https://alvinlim.me/"&gt;my website&lt;/a&gt;'s performance so that it would get a higher score than 80% on Lighthouse ... and after a few major adjustments, I managed to improve its Lighthouse performance score to 100%!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ph6yioW7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ukb73na9fv12jq0nyuji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ph6yioW7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ukb73na9fv12jq0nyuji.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How was this done? By ruthlessly minimizing the assets that needed to be loaded and rendered on the screen. In this experiment, less really proved to be a lot more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Background Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The previous version of the website featured a large background image. Even though I had optimized it by converting it to the compressed WebP format, it still remained the largest asset in the website. To maximize performance, I decided to radically change the look of my website and remove this background image entirely, and replace it with a repeating SVG pattern from &lt;a href="https://www.heropatterns.com/"&gt;Hero Patterns&lt;/a&gt;. So instead of a photo of the desert, the background of my website is now wallpaper patterned on graph paper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the previous round of optimization, I had removed the Google Analytics and Google ReCaptcha scripts. The only JavaScript script I kept was the one that built the virtual tabs for my website. To maximize performance I decided to remove this remaining piece of JavaScript.&lt;/p&gt;

&lt;p&gt;To replace its functionality, I found inspiration in &lt;a href="https://dev.to/atapas"&gt;Tapas Adhikary&lt;/a&gt;'s article &lt;a href="https://dev.to/atapas/10-useful-html5-features-you-may-not-be-using-2bk0"&gt;10 useful HTML5 features you may not be using&lt;/a&gt;, in which he showed how the Details/Summary HTML5 element could be used to selectively hide content in a website.&lt;/p&gt;

&lt;p&gt;Since this was what the old JavaScript virtual tags had done on the previous iteration of my website, I decided to use  Details/Summary HTML5 tags to selectively hide the coding projects and contact form sections of my website:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jQaeNc2d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9h8kimt0momi6wr2b2x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jQaeNc2d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9h8kimt0momi6wr2b2x7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This however altered the layout of my website but I saw this as an opportunity to rejuvenate it with a brand new look, and I spent a couple of fun hours experimenting with new fonts and color schemes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minification and Compression&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make sure that the size of the assets that would be piped from the web server to the browser remained as small as possible, I made sure that Netlify, my web host, was configured to automatically minify and compress them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t50d259s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uhvbajgb707duuo1xm2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t50d259s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uhvbajgb707duuo1xm2j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further compression comes from the &lt;a href="https://www.netlify.com/blog/2020/05/20/gain-instant-performance-boosts-as-brotli-comes-to-netlify-edge/"&gt;Brotli compression&lt;/a&gt; which is deployed automatically on Netlify's Edge network, on which websites on Netlify's generous free tier are hosted.&lt;/p&gt;

&lt;p&gt;While there are remain opportunities to further improve performance -- for example, while Netlify minifies CSS, it doesn't do so for HTML -- for the moment I'm happy with the latest round of optimizations.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>html</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Speeding up my website</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Sun, 20 Sep 2020 03:51:03 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/speeding-up-my-website-1fdl</link>
      <guid>https://dev.to/alvinqingxing/speeding-up-my-website-1fdl</guid>
      <description>&lt;p&gt;My project this weekend was to speed up my &lt;a href="https://alvinlim.me/"&gt;personal website&lt;/a&gt;. I had decided to work on this after it received a failing grade on &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Google Lighthouse&lt;/a&gt;. After a series of much-needed optimizations, the website now loads noticeably faster and its Lighthouse score is much improved:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rNUBc7sJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o5cysni0zzsqybqdnawf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rNUBc7sJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/o5cysni0zzsqybqdnawf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will now share the key optimizations that I implemented that sped up my website:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimizing Images&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The previous version of my website featured a 3 MB JPEG file which I used as the full background image. I wanted to keep the same look, which meant I had to replace this image with a format that featured better compression than JPEG. I had considered the new &lt;a href="https://jakearchibald.com/2020/avif-has-landed/"&gt;AVIF format&lt;/a&gt; but unfortunately it is currently not well-supported:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AyBpPjOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fb7ugi60nurfu3mjmagy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AyBpPjOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fb7ugi60nurfu3mjmagy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hence opted to replace the existing JPEG and PNG images with WebP images, as the WebP format is better supported:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RPIR1w2l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6l4updzlspftobfd1lkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RPIR1w2l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6l4updzlspftobfd1lkn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from changing the image format, I also ensured that the images used in my website are the exact same dimensions as they will be presented on the screen.&lt;/p&gt;

&lt;p&gt;For example, the dimensions of the portrait image I had previously used were much larger than the small 200x200 round image shown on the website. Not only did this mean precious milliseconds would be squandered downloading this unnecessarily large file, the browser too would waste time resizing the image. By deploying images of the correct dimensions, milliseconds would be saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Removing Unnecessary JavaScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apart from the need to optimize my images, Lighthouse also indicated that I could optimize my website further by removing unnecessary JavaScript scripts. I had 3 JS scripts running on my website -- a virtual tab builder; a Google Analytics script; and a Google ReCaptcha script.&lt;/p&gt;

&lt;p&gt;I decided to keep the virtual tab builder as I liked how it enhanced my website. I decided to remove the Google Analytics script as I realized that I didn't need it since I was not using my personal website for a function like ecommerce.&lt;/p&gt;

&lt;p&gt;I likewise opted to remove the Google ReCaptcha script from my &lt;a href="https://formspree.io/"&gt;FormSpree contact form&lt;/a&gt; and adjusted the form's settings to use the ReCaptcha function hosted on FormSpree instead. Removing these third-party JavaScript scripts significantly reduced the time taken for the browser to load and render the website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dumping CDNs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prior to optimization, my website made use of 3 CDNs -- &lt;a href="https://fonts.google.com/"&gt;Google Fonts&lt;/a&gt;, &lt;a href="https://fontawesome.com/"&gt;Font Awsome&lt;/a&gt;, and &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt;. Replacing these with locally hosted assets resulted in major savings of time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Google Fonts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To replace the use of the Google Font CDN, I downloaded the fonts that I needed and used &lt;a href="https://www.fontsquirrel.com/tools/webfont-generator"&gt;Font Squirrel&lt;/a&gt; to convert them to web fonts. I then moved the woff2 font files to a &lt;code&gt;fonts&lt;/code&gt; folder that I created in my website project directory and declared them in my CSS file.&lt;/p&gt;

&lt;p&gt;For example, one of the Google Fonts that I use in my website is &lt;a href="https://fonts.google.com/specimen/Righteous?query=righteous"&gt;Righteous&lt;/a&gt;. I imported this into my CSS file by declaring the @font-face (including the location):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@font-face {
  font-family: "Righteous";
  src: url(fonts/righteous-regular-webfont.woff2);
  font-display: swap;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and used it in the appropriate CSS component, in this case, the banner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.banner h1 {
  ...
  font-family: "Righteous", cursive;
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Font Awesome&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Prior to optimization, my website relied on &lt;a href="https://fontawesome.com/"&gt;Font Awesome&lt;/a&gt; to get the LinkedIn, Google, and GitHub icons used in the footer. To optimize this, I switched to &lt;a href="http://fontastic.me/"&gt;Fontastic&lt;/a&gt; which allowed me to download these icons as a single font file which I can host locally. (When you download this font file, Fontastic will give you the appropriate CSS and HTML snippets to insert in your code to display the icons.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bootstrap&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had previously included Bootstrap in my website to tweak its layout but I decided that this benefit did not justify the cost in terms of the time used to download Bootstrap from its CDN and render the Bootstrap classes in the browser. After removing Bootstrap I discovered I only had to spend just a bit of time to adjust the HTML and CSS to get the look that I wanted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Speedier Website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These optimizations were long overdue and apart from the higher score my website received from Lighthouse, the benefits could be felt in a discernibly faster website. I also tested my website in HubSpot's &lt;a href="https://website.grader.com/"&gt;Website Grader&lt;/a&gt; which gave it a higher grade than it had received before the optimization:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KDqx3LtN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/55n16ne96duo7f4sug8a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KDqx3LtN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/55n16ne96duo7f4sug8a.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>html</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Testing and Deploying a PHP App in Ubuntu</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Fri, 11 Sep 2020 17:35:18 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/testing-and-deploying-a-php-app-in-ubuntu-2i5h</link>
      <guid>https://dev.to/alvinqingxing/testing-and-deploying-a-php-app-in-ubuntu-2i5h</guid>
      <description>&lt;p&gt;If you've been learning PHP in an online classroom like &lt;a href="https://www.codecademy.com/learn/learn-php" rel="noopener noreferrer"&gt;Codecademy&lt;/a&gt; you may find yourself surprised when setting up your workstation for PHP development.&lt;/p&gt;

&lt;p&gt;Unlike HTML or JavaScript code which will run automatically in your browser, PHP code -- even if it works perfectly well in your online IDE -- will not work on localhost unless you are running it from a server.&lt;/p&gt;

&lt;p&gt;Without a server to render your PHP code, your may find your browser prompting you to download the PHP file you're trying to open, or just displaying the PHP code without rendering it. In technical terms, while JavaScript is a client-side scripting language, PHP is a &lt;em&gt;server-side&lt;/em&gt; language.&lt;/p&gt;

&lt;p&gt;Hence, to get your PHP code working in localhost, you will need to run it with a server. One popular solution is to use a prepackaged stack like &lt;a href="https://www.apachefriends.org/index.html" rel="noopener noreferrer"&gt;XAMPP&lt;/a&gt; which will contain Apache (the server), MariaDB (a popular MySQL database), and PHP. Such prepackaged stacks will save you a lot of installation time.&lt;/p&gt;

&lt;p&gt;However, if your OS is a Linux distribution like Ubuntu, you can take the opportunity to set up a classic LAMP stack (Linux, Apache, MySQL, PHP). Setting this up will however require extra configuration in addition to installing the software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MySQL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the database installation. I recommend MariaDB as it offers an optimized installation of MySQL. These commands will install MariaDB and set up the database's security options according to your preferences:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo apt update
sudo apt install mariadb-server
sudo mysql_secure_installation


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Apache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you need to install the Apache server:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo apt install apache2


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

&lt;/div&gt;

&lt;p&gt;Now that you have an Apache server running on your machine, you will need to configure Ubuntu's firewall. You can run &lt;code&gt;sudo ufw app list&lt;/code&gt; to see the available firewall profiles and &lt;code&gt;sudo ufw app info "Profile Name"&lt;/code&gt; to get information on a particular profile:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmsjwn64kyp6x0ufre2bl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmsjwn64kyp6x0ufre2bl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like a particular profile, say in this case, Apache Full, you can activate it with the command &lt;code&gt;sudo ufw allow in "Apache Full"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can now open &lt;code&gt;http://localhost/&lt;/code&gt; in your browser to see your Apache server running:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fps5wcyays1j5rk8hydfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fps5wcyays1j5rk8hydfu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, additional configuration is still needed for Apache to render your PHP code. By default, Apache will open files saved in the &lt;code&gt;/var/www&lt;/code&gt; directory. However, this directory is protected and you will need to &lt;code&gt;sudo&lt;/code&gt; to write into it. Hence it won't be good practice to keep your code here.&lt;/p&gt;

&lt;p&gt;A better solution will be to activate Apache's User Directory feature, which will allow you to run your PHP code from a new &lt;code&gt;public_html&lt;/code&gt; directory in your home folder. The following command will activate the User Directory feature:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo a2enmod userdir


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

&lt;/div&gt;

&lt;p&gt;Next, you will need create the User Directory (&lt;code&gt;~/public_html&lt;/code&gt;) and set the correct directory permissions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

mkdir $HOME/public_html
chmod +x $HOME
chmod 755 $HOME/public_html


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

&lt;/div&gt;

&lt;p&gt;You will then need to enable PHP scripting in the User Directory (by default Ubuntu disables this). To do this, first:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

cd /etc/apache2/mods-enabled/


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

&lt;/div&gt;

&lt;p&gt;When you are in the &lt;code&gt;/etc/apache2/mods-enabled/&lt;/code&gt; directory, use the command &lt;code&gt;ls&lt;/code&gt; to look for the PHP configuration file. It will be named &lt;code&gt;php[version_number].conf&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw8fig47vqp3lypknfqg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw8fig47vqp3lypknfqg3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, the version number is 7.4 and you will need to edit php7.4.conf to activate PHP scripting in the User Directory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo nano php7.4.conf


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

&lt;/div&gt;

&lt;p&gt;Look for the following lines:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;IfModule mod_userdir.c&amp;gt;
   &amp;lt;Directory /home/*/public_html&amp;gt;
       php_admin_flag engine Off
   &amp;lt;/Directory&amp;gt;
&amp;lt;/IfModule&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;Add an &lt;code&gt;#&lt;/code&gt; at the start of each line to deactivate Ubuntu's disabling of PHP scripting in the User Directory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# &amp;lt;IfModule mod_userdir.c&amp;gt;
#    &amp;lt;Directory /home/*/public_html&amp;gt;
#        php_admin_flag engine Off
#    &amp;lt;/Directory&amp;gt;
# &amp;lt;/IfModule&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;The file should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feu7mf5eg5vtzvp2g3gzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feu7mf5eg5vtzvp2g3gzd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;Control-O&lt;/code&gt; to save the file and then &lt;code&gt;Control-X&lt;/code&gt; to exit the text editor.&lt;/p&gt;

&lt;p&gt;In that same directory, you will also need to edit another Apache configuration file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo nano /etc/apache2/mods-enabled/dir.conf


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

&lt;/div&gt;

&lt;p&gt;Look for this line:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm


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

&lt;/div&gt;

&lt;p&gt;In this line, cut and paste index.php so that it comes &lt;em&gt;before&lt;/em&gt; index.html:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm


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

&lt;/div&gt;

&lt;p&gt;Save the file and exit. You will now need to restart Apache to active the new settings:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo systemctl restart apache2


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The final part of the LAMP stack is of course PHP:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo apt install php libapache2-mod-php php-mysql


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

&lt;/div&gt;

&lt;p&gt;This command will install PHP as well as the packages needed for PHP to access Apache as well as MariaDB.&lt;/p&gt;

&lt;p&gt;You should now be able to run your PHP code in your new LAMP stack. To test this, write and save a PHP file in your &lt;code&gt;public_html&lt;/code&gt; directory. Here's a very simple -- but useful! -- PHP script you can use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php
    phpinfo();


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

&lt;/div&gt;

&lt;p&gt;Save this in your &lt;code&gt;public_html&lt;/code&gt; directory. I have named mine &lt;code&gt;test.php&lt;/code&gt; but you can name it anything you want; just make sure the filename has the &lt;code&gt;.php&lt;/code&gt; suffix.&lt;/p&gt;

&lt;p&gt;Open your browser and go the following address (replace &lt;code&gt;username&lt;/code&gt; with your Ubuntu username and &lt;code&gt;filename&lt;/code&gt; with the name you gave your test file):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

http://localhost/~username/filename.php


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

&lt;/div&gt;

&lt;p&gt;In my case, the URL is &lt;code&gt;http://localhost/~alvin/test.php&lt;/code&gt;. My LAMP stack has successfully rendered the PHP script in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwixjj51u51pvpz1tum5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwixjj51u51pvpz1tum5f.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying to Production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Given these complicated steps just to render a PHP script in localhost, you might imagine that deploying a PHP app would be similarly complicated, and you would be right!&lt;/p&gt;

&lt;p&gt;Apps written in PHP will not run on platforms like Netlify which are designed to host static apps. While you can host and run your PHP app on Heroku, it has to be deployed with a &lt;code&gt;composer.json&lt;/code&gt; companion file otherwise the deployment will fail. To generate a &lt;code&gt;composer.json&lt;/code&gt; file, you will need to use install and use Composer. Please refer to &lt;a href="https://getcomposer.org/doc/00-intro.md" rel="noopener noreferrer"&gt;its documentation&lt;/a&gt; for instructions on how to do this.&lt;/p&gt;

&lt;p&gt;The purpose of &lt;code&gt;composer.json&lt;/code&gt; is to keep track of the dependencies of your PHP app, but even if your app has no such dependencies, you will still need to generate a &lt;code&gt;composer.json&lt;/code&gt; file if you want to deploy your app to Heroku. My &lt;a href="https://magic-8-ball-php.herokuapp.com/" rel="noopener noreferrer"&gt;Magic 8-Ball&lt;/a&gt; PHP app, which is hosted on Heroku, has one such dependency-free &lt;a href="https://github.com/alvinqingxing/magic-8-ball/blob/master/composer.json" rel="noopener noreferrer"&gt;composer.json&lt;/a&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkz53zcbt6xqyijjnm1tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkz53zcbt6xqyijjnm1tj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it's important to keep the goal in mind. Once you get past the hurdle of creating a &lt;code&gt;composer.json&lt;/code&gt; file, you will have a shiny new PHP app for your portfolio!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foe8k09lu7y9my45izts8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foe8k09lu7y9my45izts8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>codenewbie</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Coding with No Code</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Thu, 30 Jul 2020 13:54:13 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/coding-with-no-code-12gd</link>
      <guid>https://dev.to/alvinqingxing/coding-with-no-code-12gd</guid>
      <description>&lt;p&gt;I'm working through an intense React.js course and I decided to take a break and check out some of the no-code platforms which have come into prominence again thanks to the recent launch of &lt;a href="https://www.honeycode.aws/" rel="noopener noreferrer"&gt;AWS Honeycode&lt;/a&gt;. I signed up for Bubble.io thanks to &lt;a href="https://bubble.io/academy" rel="noopener noreferrer"&gt;its tutorials&lt;/a&gt; and had my preconceptions shattered about what a no-code app could do.&lt;/p&gt;

&lt;p&gt;One of the first projects of the React.js course I'm doing was the creation of a Giphy clone, so I was amused to find that creating a Giphy clone was actually one of Bubble.io's &lt;a href="https://bubble.io/lessons" rel="noopener noreferrer"&gt;3-minute tutorials&lt;/a&gt;. When I worked on this further as &lt;a href="https://bubble-giphy-clone.bubbleapps.io/" rel="noopener noreferrer"&gt;my own project&lt;/a&gt; I only had to spend a few more minutes to get the images to be properly sized for mobile:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhyi9p2vjtfrzj5jd2vwi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhyi9p2vjtfrzj5jd2vwi.png" alt="Alt Text"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;A separate to-do list app that I worked on showed that no-code apps can have CRUD functionality. Indeed, Bubble.io's collection of tutorials include how-tos on creating &lt;a href="https://bubble.io/how-to-build" rel="noopener noreferrer"&gt;no-code versions of major apps&lt;/a&gt; like AirBnB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2hvkhz1ljm8c71v38gbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2hvkhz1ljm8c71v38gbc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what is the no-code experience like? As promised, there is no code -- you just wireframe and fill out how you want your app to look -- the inputs, buttons, images, etc. -- and then you click on each component to specify the back-end logic of how they should connect with the database, with one another, or with external plugins. Instead of code you use a GUI to describe the internal logic of your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnxatyzp9q33221258p8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnxatyzp9q33221258p8b.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that sense no-code programming is a natural extension of coding, since a successful no-code app will still need to have a consistent internal logic. Instead of a programming language, the coder's pseudocode will be implemented through the no-code platform's GUI. No-code can hence be seen as just a further abstraction rather than a radical break from coding.&lt;/p&gt;

&lt;p&gt;While it's as yet unclear how far the no-code movement will go, given how quickly apps can be created and deployed on no-code platforms, this is definitely a tool that coders should have in their toolkits.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Writing an API in Rails 6</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Tue, 28 Jul 2020 15:25:47 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/writing-an-api-in-rails-6-37o4</link>
      <guid>https://dev.to/alvinqingxing/writing-an-api-in-rails-6-37o4</guid>
      <description>&lt;p&gt;In the final stage of my &lt;a href="https://dev.to/alvinqingxing/so-i-just-completed-coding-bootcamp-30ep"&gt;coding bootcamp&lt;/a&gt;, when I had become familiar with Ruby on Rails, one project I wanted to do was to write my own API.&lt;/p&gt;

&lt;p&gt;APIs (Application Programming Interfaces) are widely used on the internet for computers to send data to one another. Unlike a standard web page, where the server sends a bundle of HTML, CSS, and JavaScript to create a view which is consumed by a human audience, with APIs it is machines communicating with one another.&lt;/p&gt;

&lt;p&gt;So if you are viewing a web application that requires data from an API, your browser will send the request to the API, get the requested data back, and then &lt;em&gt;render&lt;/em&gt; that data into a format that is suitable for you, a human, to view.&lt;/p&gt;

&lt;p&gt;Hence, instead of HTML and CSS, APIs send data in more machine-friendly formats such as JSON, YAML, or XML.&lt;/p&gt;

&lt;p&gt;For my API, I wanted to do something fun. My coding class had previously learned how to use &lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker&lt;/a&gt; to seed our databases with mock information, and I recalled that one of Faker's libraries consisted of Chuck Norris jokes. I decided to go with this and create an API that sends out Chuck Norris jokes.&lt;/p&gt;

&lt;p&gt;Let's build the API! First I create the directory and go into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir chuck-norris-api
$ cd chuck-norris-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once in the directory, I instruct Rails to generate the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails new chuck-norris-api --api --database=postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--api&lt;/code&gt; option (which is available in Rails 6) tells Rails that you don't want it to generate a full app -- only enough for an API to run. I set the second option &lt;code&gt;--database=postgresql&lt;/code&gt; as I will be using Heroku to host the API, and Heroku does not support Rails' default SQLite database.&lt;/p&gt;

&lt;p&gt;Once the app is generated, I open my code editor and edit the &lt;code&gt;Gemfile&lt;/code&gt; located at the root of the directory. I add &lt;code&gt;gem 'faker'&lt;/code&gt; and uncomment &lt;code&gt;gem 'rack-cors'&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7bk7z822wof88ia7ncn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7bk7z822wof88ia7ncn5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I explained earlier, I will be using &lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker&lt;/a&gt; to populate my database with Chuck Norris jokes, while &lt;a href="https://github.com/cyu/rack-cors" rel="noopener noreferrer"&gt;Rack CORS&lt;/a&gt; is the middleware I need to ensure that the front-end I will be building to  display the jokes will be able to receive the jokes from the API (more on that later).&lt;/p&gt;

&lt;p&gt;I then run &lt;code&gt;bundle install&lt;/code&gt; in the terminal to install the new gems. Once that's done, it's time to build out the app's MVC!&lt;/p&gt;

&lt;p&gt;First, the &lt;strong&gt;models&lt;/strong&gt;. In this case, there's only one: the jokes. Each joke has one field: its &lt;em&gt;content&lt;/em&gt;. In the terminal I instruct Rails to generate the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails g model Joke content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This create the following migration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F04wcq7rmzt8hyk0yitvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F04wcq7rmzt8hyk0yitvc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal I instruct Rails to run the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the Jokes table in the database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh8do6qk9dndxga923u9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh8do6qk9dndxga923u9r.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database can now be populated with the Chuck Norris jokes. In &lt;code&gt;seeds.rb&lt;/code&gt; I instruct Rails to retrieve a hundred jokes from &lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker's&lt;/a&gt; Chuck Norris library:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcumfm0vwv62ijxbgmh8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcumfm0vwv62ijxbgmh8i.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, I then instruct Rails to run &lt;code&gt;seeds.rb&lt;/code&gt; and populate the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails db:seed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the &lt;strong&gt;model&lt;/strong&gt; and database are sorted, I can proceed with the &lt;strong&gt;controller&lt;/strong&gt; and &lt;strong&gt;view&lt;/strong&gt; (the remaining two-thirds of the app's MVC). In the &lt;code&gt;app\controllers&lt;/code&gt; folder I create the nested folders &lt;code&gt;api\v1&lt;/code&gt;. In the &lt;code&gt;v1&lt;/code&gt; folder I create the &lt;code&gt;jokes_controller.rb&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7fq0shqhggt7tm8zews1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7fq0shqhggt7tm8zews1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my controller, I only allow the user to perform two actions: get &lt;em&gt;all&lt;/em&gt; jokes, and get &lt;em&gt;one&lt;/em&gt; particular joke. I do not want the user to edit or delete existing jokes or to add new jokes. This is for the simple reason that I do not want to have to keep checking the database for inappropriate content once the API is deployed. Hence I do not include these remaining &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="noopener noreferrer"&gt;CRUD operations&lt;/a&gt; in the controller.&lt;/p&gt;

&lt;p&gt;As with the controller, I add nested folders (&lt;code&gt;api\v1\jokes&lt;/code&gt;) in the &lt;code&gt;app\views&lt;/code&gt; folder. The &lt;code&gt;api\v1\jokes&lt;/code&gt; folder has two files corresponding to the two controller actions. The first is the &lt;code&gt;index.json.jbuilder&lt;/code&gt; file which will build and return a JSON file containing an array of all one hundred Chuck Norris jokes when a request for all jokes is received. In particular, I instruct the JSON builder to &lt;em&gt;only&lt;/em&gt; include the content of each joke in the array, and leave out the other fields such as the id numbers of the jokes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7xjjwhb6fkyp2hsdylg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7xjjwhb6fkyp2hsdylg9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second is the &lt;code&gt;show.json.jbuilder&lt;/code&gt; file which will return the joke with the particular id sent by the controller. As with the index file, I instruct the JSON builder to only include the content of the joke in the JSON file that is sent back to the user:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpysq7xq78uqofbpb60i8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpysq7xq78uqofbpb60i8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the MVC are done, we can now proceed to the routes. In the &lt;code&gt;config\routes.rb&lt;/code&gt; file I specify the nested &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;v1&lt;/code&gt; namespaces that correspond to the nested &lt;code&gt;api\v1&lt;/code&gt; folders where the controller is housed, and I also specify that the only actions offered by the API are the &lt;code&gt;index&lt;/code&gt; (get &lt;em&gt;all&lt;/em&gt; jokes) and &lt;code&gt;show&lt;/code&gt; (get &lt;em&gt;one&lt;/em&gt; joke) actions, rather than the full suite of &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="noopener noreferrer"&gt;CRUD operations&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1m0sjwr8o0chwspzh1dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1m0sjwr8o0chwspzh1dy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is one final step that I need to do before I can deploy the app to production. It is a security feature of modern web browsers that, by default, they do not retrieve data from APIs if the domain of the requesting web page is different from the domain of the API. This will only be allowed if the API specifically allows web pages hosted on that particular domain to retrieve its data.&lt;/p&gt;

&lt;p&gt;The obvious way to get around this restriction is to host the web page on the same domain as the API. But what is the fun in that? For this project, the API will be hosted on Heroku, and the web page which I will be displaying the jokes will be hosted on GitHub Pages. This is where the &lt;a href="https://github.com/cyu/rack-cors" rel="noopener noreferrer"&gt;Rack CORS&lt;/a&gt; gem that had been installed earlier comes in. In the &lt;code&gt;config\initializers\cors.rb&lt;/code&gt; file I specify that my GitHub Pages domain is to be allowed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F90vpztr8doxsxlpzblw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F90vpztr8doxsxlpzblw7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it is certainly possible to set &lt;code&gt;origins '*'&lt;/code&gt; in &lt;code&gt;cors.rb&lt;/code&gt; and allow &lt;em&gt;any&lt;/em&gt; website to access the API, this will effectively bypass the security protocol. So I will be following the best practice and curate which domains are allowed to use the API. It should be noted that this is a security feature of web browers. As we shall see, if you use a tool like &lt;a href="https://curl.haxx.se/docs/manpage.html" rel="noopener noreferrer"&gt;curl&lt;/a&gt; or &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; to access the API, this cross-domain security block will not apply.&lt;/p&gt;

&lt;p&gt;The app is now ready to be deployed! It can be accessed at &lt;code&gt;https://rails-chuck-norris-api.herokuapp.com&lt;/code&gt; and as noted, you can use &lt;code&gt;curl&lt;/code&gt; from your terminal to retrieve all the jokes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -s https://rails-chuck-norris-api.herokuapp.com/api/v1/jokes | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... or any one of the one hundred jokes. Here &lt;code&gt;curl&lt;/code&gt; retrieves joke #99:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -s https://rails-chuck-norris-api.herokuapp.com/api/v1/jokes/99 | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But as noted, I have created &lt;a href="https://alvinqingxing.github.io/chuck-norris-front/" rel="noopener noreferrer"&gt;a nice front-end&lt;/a&gt; at GitHub Pages to retrieve and display the jokes. The source code can be &lt;a href="https://github.com/alvinqingxing/chuck-norris-front" rel="noopener noreferrer"&gt;viewed here&lt;/a&gt;, but the essential part is this JavaScript code which generates a random number between 1 and 100 and retrieves the joke with that id from the API:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq5ke0pbomd6qp5levmc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq5ke0pbomd6qp5levmc5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chuck Norris has the final word:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxvt8msfw1xvouw7gvfvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxvt8msfw1xvouw7gvfvl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found this to be a really fun project to do, and I hope it will inspire you to create your own API!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>webdev</category>
    </item>
    <item>
      <title>So I just completed coding bootcamp...</title>
      <dc:creator>Alvin Lim</dc:creator>
      <pubDate>Mon, 20 Jul 2020 15:11:22 +0000</pubDate>
      <link>https://dev.to/alvinqingxing/so-i-just-completed-coding-bootcamp-30ep</link>
      <guid>https://dev.to/alvinqingxing/so-i-just-completed-coding-bootcamp-30ep</guid>
      <description>&lt;p&gt;I've just completed the most gruelling 9 weeks of my life -- Le Wagon Singapore's &lt;a href="https://www.lewagon.com/singapore/web-development-course/full-time"&gt;web developer bootcamp&lt;/a&gt;. I started as a complete code newbie and now have several web apps deployed to Heroku, including my coding team's beautiful capstone project &lt;a href="https://www.savvy-granny.com"&gt;Savvy Granny&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z4bcOZwU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z4wv6a06fq647r96zait.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z4bcOZwU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z4wv6a06fq647r96zait.jpg" alt="Savvy Granny"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am a mid-career convert to the tech sector. In my earlier career I was an academic. After publishing two books, I felt burned out and wanted to create and build things instead of analyze or write about them. The idea of coding bootcamp appealed to me, but with my liberal arts background, it just seemed impossibly difficult.&lt;/p&gt;

&lt;p&gt;The turning point for me was when I came across a post from Le Wagon's co-founder Sébastien Saunier explaining why &lt;a href="https://medium.com/le-wagon/why-learn-ruby-on-rails-9862354c9ce6"&gt;they taught Ruby&lt;/a&gt;. His description of Ruby as "simple, concise, close to the English language and very flexible" intrigued me, and I soon signed up for Codecademy's &lt;a href="https://www.codecademy.com/learn/learn-ruby"&gt;free Ruby course&lt;/a&gt;. I decided that if I could learn Ruby, then I could handle a coding bootcamp.&lt;/p&gt;

&lt;p&gt;As it turned out, I not only completed Codecademy's Ruby course, I found it incredibly fun! Coding was not at all what I had previously imagined it to be. I signed up for Le Wagon's web developer bootcamp, and began learning as much as I could about technology, almost as if to make up for lost time.&lt;/p&gt;

&lt;p&gt;The bootcamp itself was a blast. Everyday we did a series of &lt;a href="https://www.codewars.com/"&gt;Codewars-type&lt;/a&gt; coding challenges, and we began building things with code. One of the earliest and most gratifying moments for me was when I built &lt;a href="https://alvinlim.me/"&gt;my personal website&lt;/a&gt; with HTML, CSS, and JavaScript and deployed it to GitHub Pages. I had built my previous website with WordPress' drag-and-drop interface -- lacking at that time any knowledge of CSS -- so I felt a great sense of personal accomplishment in coding my own website.&lt;/p&gt;

&lt;p&gt;Once we moved into Ruby on Rails, we began building apps that we could deploy on Heroku. We started small -- simple productivity tools like &lt;a href="https://lewagon417-todo-list.herokuapp.com/"&gt;to-do lists&lt;/a&gt;, and then we moved into larger apps like the &lt;a href="https://lewagon417-mister-cocktail.herokuapp.com/"&gt;Mr. Cocktail&lt;/a&gt; mixology app. Along the way, I had gained the confidence to start building my own projects, including this website hosted on Github Pages which displays &lt;a href="https://alvinqingxing.github.io/chuck-norris-front/"&gt;random Chuck Norris jokes&lt;/a&gt; received from its corresponding API hosted on Heroku.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Ox4OEob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j3k1a3wnorpghps8vqay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Ox4OEob--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j3k1a3wnorpghps8vqay.png" alt="Chuck Norris"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The climax of the bootcamp was the two weeks spent building our capstone projects. I was extremely fortunate to be grouped with three talented developers, and we quickly built the guts of the app in the first week, and methodically built out the special features of the app in the second. I was especially pleased that our app serves the urgent social need of bridging the generational digital divide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--40Syn3fk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pxgxcko0wl8k3stndtr2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--40Syn3fk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pxgxcko0wl8k3stndtr2.jpg" alt="Savvy Granny"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the bootcamp is over, my fellow grads and I have the technical foundation to learn and build more. Our familiarity with the Rails framework has given us the confidence to move into other frameworks, in particular React. I for one am excited about the new apps that we are going to build.&lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
      <category>codenewbie</category>
    </item>
  </channel>
</rss>
