<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Chidiadi Anyanwu - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Chidiadi Anyanwu - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 09 May 2026 16:24:33 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/chidiadi01/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Save Multiple Drafts in Git: A Guide to Using Stash ]]>
                </title>
                <description>
                    <![CDATA[ Writing code can be similar to writing tutorials. In both cases, you’ll typically need to create and work on multiple drafts before reaching the final version. In an ideal setting, you would write cod ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-save-multiple-drafts-in-git-a-guide-to-using-stash/</link>
                <guid isPermaLink="false">6989f72cfec80c4e91c007a4</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ version control ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Mon, 09 Feb 2026 15:03:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770649372924/7c949e9d-627a-4b06-99fe-40ce0e7c8507.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Writing code can be similar to writing tutorials. In both cases, you’ll typically need to create and work on multiple drafts before reaching the final version.</p>
<p>In an ideal setting, you would write code for a feature, add that code to the staging area, and then commit it before going to the next part. This helps keep your commit history clean. (Don't worry if you don't have a clean history. Many of us don’t.)</p>
<p>But now, imagine a scenario where you have multiple features to build. You've committed the first. You've started the third, but then you found out you needed to build the second one first, because the third depends on it. It might seem like you have to go back in time and build out that second feature without mixing in the code changes for the third, and without deleting the code changes for the third.</p>
<p>So how do you do that?</p>
<p>In this article, you’re going to learn about Git stash, what it is, and the basic commands you need to be able to use it.</p>
<h2 id="heading-what-well-cover">What We’ll Cover:</h2>
<ul>
<li><p><a href="#heading-what-is-git-stash">What is Git Stash?</a></p>
</li>
<li><p><a href="#heading-how-to-use-git-stash">How to Use Git Stash</a></p>
<ul>
<li><p><a href="#heading-pushing-commands-to-the-stash">Pushing Commands To The Stash</a></p>
</li>
<li><p><a href="#heading-applying-changes-from-the-stash-to-the-working-directory">Applying Changes From The Stash To The Working Directory</a></p>
</li>
<li><p><a href="#heading-listing-the-items-in-the-stash-list">Listing The Items In The Stash List</a></p>
</li>
<li><p><a href="#heading-removing-items-from-the-stash">Removing Items From The Stash</a></p>
</li>
<li><p><a href="#heading-creating-a-new-branch-from-stash">Creating A New Branch From Stash</a></p>
</li>
<li><p><a href="#heading-showing-the-changes-in-the-stash">Showing The Changes In The Stash</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>To understand and get the most out of this tutorial, you’ll need to have a basic understanding of Git.</p>
<h2 id="heading-what-is-git-stash">What is Git Stash?</h2>
<p>Git stash provides storage (in the form of a stack) where you can store changes to your code. It lets you keep these changes separate from the current working directory until you're ready to apply them.</p>
<p><code>git stash</code> is a relatively simple command, and has a few variations like:</p>
<ul>
<li><p><code>git stash push</code></p>
</li>
<li><p><code>git stash pop</code></p>
</li>
<li><p><code>git stash apply</code></p>
</li>
<li><p><code>git stash drop</code></p>
</li>
<li><p><code>git stash clear</code></p>
</li>
<li><p><code>git stash show</code></p>
</li>
<li><p><code>git stash list</code></p>
</li>
</ul>
<h2 id="heading-how-to-use-git-stash">How to Use Git Stash</h2>
<h3 id="heading-pushing-code-changes-to-the-stash">Pushing Code Changes to the Stash</h3>
<p>To push uncommitted changes from the working directory to the stash, you can use <code>git stash push.</code> When you do that, the changes disappear from the working directory and are saved in the stash. The working directory is then set back to the last commit (which, in the example below, is the Feature one).</p>
<pre><code class="language-bash">git stash push
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/7652801b-cb1e-45cc-ac94-281512252f37.png" alt="7652801b-cb1e-45cc-ac94-281512252f37" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You can also write the <code>git stash push</code> command as just <code>git stash</code> – it means the same thing and performs the same way. You can also add a message to it with the <code>-m</code> or <code>--message</code> flag:</p>
<pre><code class="language-bash">git stash push -m "Feature two I was working on"
</code></pre>
<p>You can use the <code>-u</code> flag or <code>--include-untracked</code> to include untracked changes in the stash. This way, the changes not yet tracked by Git can be included in the stash.</p>
<pre><code class="language-bash">git stash push -u
git stash push --include-untracked
</code></pre>
<p>On the other hand, you can decide to push only staged changes to the stash with the <code>-s</code> or <code>--staged</code> flag:</p>
<pre><code class="language-bash">git stash push -s
</code></pre>
<p>You can also suppress feedback messages with the <code>-q</code> or <code>--quiet</code> flag:</p>
<pre><code class="language-bash">git stash push -q
</code></pre>
<p>When you're done with your edits on the current working directory, you can commit and push without the older changes bleeding into it. They're safely stowed away in the stash.</p>
<p>Anytime you use the git stash command, you add a stash to the stash list. The stashes are identified by their index numbers.</p>
<h3 id="heading-applying-changes-from-the-stash-to-the-working-directory">Applying Changes from the Stash to the Working Directory</h3>
<p>Let’s say you had a quick fix to do. You’re done committing that, and you want to continue with what you were working on. You can restore the changes from the stash to continue working on them.</p>
<p>You can do this by using the <code>git stash apply</code> command or the <code>git stash pop</code> command:</p>
<pre><code class="language-bash">git stash apply
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/89d30b86-ba75-412f-8aba-b8bf1ac32933.png" alt="89d30b86-ba75-412f-8aba-b8bf1ac32933" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p><code>git stash apply</code> applies the latest changes from the stash to the working directory, but doesn’t delete the code from the stash. You can select a particular entry to apply by index number:</p>
<pre><code class="language-bash">git stash apply --index stash@{1}
# You'd need to use quotes "stash@{1}" if you're writing this in PowerShell
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/4a26b84c-9bf0-4bd2-9687-7ebb94046f5d.png" alt="4a26b84c-9bf0-4bd2-9687-7ebb94046f5d" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p><code>git stash pop</code>, on the other hand, applies the latest changes from the stash, then deletes them from the stash. Basically, popping from a stack. You can select a particular stash entry to pop by index number.</p>
<pre><code class="language-bash">git stash pop
</code></pre>
<pre><code class="language-bash">git stash pop --index stash@{1}
# You'd need to use quotes "stash@{1}" if you're writing this in PowerShell
</code></pre>
<h3 id="heading-listing-the-items-in-the-stash-list">Listing the Items in the Stash List</h3>
<p>You can use <code>git stash list</code> to list out the stashes in the stash list. It’s arranged by index number (like {0}, {1}, and so on). Any time you do a git stash push, it adds a stash to the stash list.</p>
<pre><code class="language-bash">git stash list
stash@{0}: WIP on master: b2a2709 Feature one
stash@{1}: WIP on master: b2a2709 Feature one
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/46c7b4a8-6701-4227-8d48-9da354c9d1b7.png" alt="46c7b4a8-6701-4227-8d48-9da354c9d1b7" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-removing-items-from-the-stash">Removing Items from the Stash</h3>
<p>You can use <code>git stash clear</code> to clear the stash list. But if you just want to drop a particular entry from the stash list, you can use <code>git stash drop</code> and specify the entry you want to drop by the index number. This doesn’t apply its changes to the working directory.</p>
<pre><code class="language-bash">git stash clear
</code></pre>
<pre><code class="language-bash">git stash drop stash@{0}
# You'd need to use quotes "stash@{0}" if you're writing this in PowerShell
</code></pre>
<h3 id="heading-creating-a-new-branch-from-stash">Creating a New Branch from Stash</h3>
<p>You can also create a new branch from a stash using <code>git stash branch</code>. The syntax is <code>git stash branch &lt;branchname&gt; [&lt;stash&gt;].</code></p>
<pre><code class="language-bash">git stash branch premium-branch stash@{0}
# You'd need to use quotes "stash@{0}" if you're writing this in PowerShell
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/a14db1c1-b0ac-4a81-ae4d-4b4859989489.png" alt="a14db1c1-b0ac-4a81-ae4d-4b4859989489" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>If you don’t add the stash index, it will just use the last stash.</p>
<pre><code class="language-bash">git stash branch premium-branch
</code></pre>
<h3 id="heading-showing-the-changes-in-the-stash">Showing the Changes in the Stash</h3>
<p>You can use <code>git stash show</code> to show the changes you’ve made in your stashed changes.</p>
<pre><code class="language-bash">C:\file-path\git_stash_example&gt; git stash show
 text.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/9d5a5e83-7c2f-4a2d-a004-f406b5ed1af7.png" alt="9d5a5e83-7c2f-4a2d-a004-f406b5ed1af7" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-conclusion">Conclusion</h2>
<p>Git stash is one of those quiet tools that becomes indispensable once your workflow starts to get messy. It allows you to shelve unfinished ideas, switch context without panic, and keep your commits clean. With it, you can safely carry out urgent fixes, and juggle dependent features without muddling up your commit history.</p>
<p>If you enjoyed this article, share it with others. You can also reach me on <a href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or <a href="https://linkedin.com/in/chidiadi-anyanwu">X.</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How To Deploy a Next.js App To Vercel With GitHub Actions ]]>
                </title>
                <description>
                    <![CDATA[ Vercel is a cloud platform or Platform-as-a-Service (PaaS) designed to help frontend developers create, preview, and deploy web applications swiftly and efficiently. In this tutorial, we’ll focus on deploying a Next.js application to Vercel using Git... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/deploy-to-vercel-with-github-actions/</link>
                <guid isPermaLink="false">684871b16bc1898625c952fd</guid>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Tue, 10 Jun 2025 17:56:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749577622920/8e35a6c1-3f4f-49a3-a4fe-dba80e24eec3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Vercel is a cloud platform or Platform-as-a-Service (PaaS) designed to help frontend developers create, preview, and deploy web applications swiftly and efficiently. In this tutorial, we’ll focus on deploying a Next.js application to Vercel using GitHub Actions.</p>
<p>In a <a target="_blank" href="https://www.freecodecamp.org/news/how-to-build-a-simple-portfolio-blog-with-nextjs/">previous article</a>, we built a Next.js portfolio blog. Here, you’ll learn how to deploy it on Vercel with <a target="_blank" href="https://www.freecodecamp.org/news/automate-cicd-with-github-actions-streamline-workflow/">GitHub Actions</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To be able to deploy your project, you should have a GitHub repository of the project (you can still follow along if you already have a Next.js project), and a Vercel account. <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio">Here is the GitHub repository that we’ll be working with</a>. You can clone it to follow along.</p>
<h2 id="heading-how-to-deploy-your-next-app">How to Deploy Your Next App</h2>
<h3 id="heading-create-vercel-token-and-add-it-to-your-secrets-in-github">Create Vercel Token and Add it to Your Secrets in GitHub</h3>
<p>In your Vercel account, go to Settings, then go to Tokens.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749036906930/1c483351-0e78-4392-a948-53921ba2916c.png" alt="Vercel account settings tokens." class="image--center mx-auto" width="1354" height="602" loading="lazy"></p>
<p>In the Create Token section, enter a name for your token, select an expiration date and click “create”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749037009419/2a48b48d-31c4-4b72-a281-dd8eb689770d.png" alt="Creating a vercel token" class="image--center mx-auto" width="1347" height="593" loading="lazy"></p>
<p>You should see a success message with your token. Next, go to your GitHub repository, and click on the “Settings“ tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749037335441/1a46fb8b-8f3c-4d44-8ad5-c7de3340ef2b.png" alt="Vercel, token created success message." class="image--center mx-auto" width="1339" height="599" loading="lazy"></p>
<p>In the Settings tab, go to Secrets and Variables on the sidebar, then click on Actions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749037726010/5ca33111-0dbe-4e3e-bde5-91a8e518f05b.png" alt="Actions secrets in GitHub repository settings." class="image--center mx-auto" width="1100" height="601" loading="lazy"></p>
<p>You’ll see a section for adding secrets. Add a secret named <code>VERCEL_TOKEN</code>, and paste the token there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749037787198/53b42a96-ad79-4895-aba0-abe6d79eceb5.png" alt="vercel token, project id, org id." class="image--center mx-auto" width="880" height="334" loading="lazy"></p>
<p>The Vercel token is a token used to authenticate the GitHub runner. The Vercel CLI installed on the GitHub runner is going to execute the commands with your account. So, instead of it having to login, it uses the access token to verify that it was actually authorized by you to take the actions.</p>
<p>The Organization ID is used to tell Vercel which organization or team account the project should be created under.</p>
<p>The Project ID then tells Vercel the specific project you want to deploy. Just like the Organization ID, it is a unique identifier.</p>
<h3 id="heading-install-the-vercel-cli-and-login">Install the Vercel CLI and Login</h3>
<p>Use the command below to install vercel CLI globally on your computer:</p>
<pre><code class="lang-bash">npm install -g vercel
</code></pre>
<p>Then <a target="_blank" href="https://vercel.com/docs/cli/login">log into the CLI</a> with the following command:</p>
<pre><code class="lang-bash">vercel login
</code></pre>
<p>Use one of the options to login.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749312895981/93c13b75-83da-4da7-b5c5-17572d126ce4.png" alt="login methods" class="image--center mx-auto" width="762" height="159" loading="lazy"></p>
<p>I used GitHub. Select one with your arrow keys, and click enter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749312974078/2f470d5a-9e73-44be-b520-5179da61f86b.png" alt="login success" class="image--center mx-auto" width="1335" height="571" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749038078744/bbb5a43d-66a4-4531-a27c-12442c977568.png" alt="vercel login" class="image--center mx-auto" width="921" height="140" loading="lazy"></p>
<h3 id="heading-create-a-vercel-project-from-your-local-directory">Create a Vercel Project from Your Local Directory</h3>
<p>Navigate to your project directory if you’re not already in it. If you have already created a project on Vercel through the web interfce, use the <a target="_blank" href="https://vercel.com/docs/cli/link">vercel link</a> command to link your current directory to the Vercel project. If you don’t already have a Vercel project, just type <code>vercel</code> in the CLI and follow the prompts to setup the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749314606959/f6d7a71c-edc5-48f2-81ca-4fd3a92c44da.png" alt="Create new Vercel project" class="image--center mx-auto" width="604" height="52" loading="lazy"></p>
<p>With that, Vercel will create a <code>.vercel</code> folder in the project. Open it, and go to the <code>project.json</code> file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749038798352/23d8b1b9-5bbf-43df-9444-7adf1f7a9c2f.png" alt="Project.json" class="image--center mx-auto" width="232" height="105" loading="lazy"></p>
<p>In the file, you should see your project ID and organization ID. Copy them and create secrets in your GitHub repository for each one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749037787198/53b42a96-ad79-4895-aba0-abe6d79eceb5.png" alt="vercel token, org ID, project ID." class="image--center mx-auto" width="880" height="334" loading="lazy"></p>
<h3 id="heading-create-your-github-workflow-file">Create your GitHub Workflow File</h3>
<p>At the root of your project folder, create the <code>.github/workflow</code> folder. Then create a workflow file called <code>vercel_deploy.yml</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749039652451/f3c3a4ca-5d19-4866-a69d-9f4d3a760d8f.png" alt="f3c3a4ca-5d19-4866-a69d-9f4d3a760d8f" class="image--center mx-auto" width="233" height="45" loading="lazy"></p>
<p>In the file, write this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Vercel</span> <span class="hljs-string">Production</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">env:</span>
  <span class="hljs-attr">VERCEL_ORG_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.VERCEL_ORG_ID</span> <span class="hljs-string">}}</span>
  <span class="hljs-attr">VERCEL_PROJECT_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.VERCEL_PROJECT_ID</span> <span class="hljs-string">}}</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'01-simple-blog/**'</span>  

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">Deploy-Production:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">defaults:</span>
      <span class="hljs-attr">run:</span>
        <span class="hljs-attr">working-directory:</span> <span class="hljs-number">01</span><span class="hljs-string">-simple-blog</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Vercel</span> <span class="hljs-string">CLI</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">--global</span> <span class="hljs-string">vercel@latest</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Pull</span> <span class="hljs-string">Vercel</span> <span class="hljs-string">Environment</span> <span class="hljs-string">Information</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">pull</span> <span class="hljs-string">--yes</span> <span class="hljs-string">--environment=production</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">Project</span> <span class="hljs-string">Artifacts</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">build</span> <span class="hljs-string">--prod</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">continue-on-error:</span> <span class="hljs-literal">true</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Project</span> <span class="hljs-string">Artifacts</span> <span class="hljs-string">to</span> <span class="hljs-string">Vercel</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">deploy</span> <span class="hljs-string">--prebuilt</span> <span class="hljs-string">--prod</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<p>This is the workflow file for my <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio/blob/main/.github/workflows/vercel_deploy.yml">simple-writer-portfolio</a> project.</p>
<p>First, we have the environment variables:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">env:</span>
  <span class="hljs-attr">VERCEL_ORG_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.VERCEL_ORG_ID</span> <span class="hljs-string">}}</span>
  <span class="hljs-attr">VERCEL_PROJECT_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.VERCEL_PROJECT_ID</span> <span class="hljs-string">}}</span>
<span class="hljs-comment"># Other code</span>
</code></pre>
<p>Then we have the trigger. This triggers when I push to the main branch, affecting files in the <code>01-simple-blog</code> subdirectory.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Previous code</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'01-simple-blog/**'</span>  
<span class="hljs-comment"># Other code</span>
</code></pre>
<p>Then we have the job definition. Here, I defined a job “Deploy-Production” that runs on Ubuntu. By default, all commands there will run in the <code>01-simple-blog</code> directory, which is equivalent to running <code>cd 01-simple-blog</code> from the root before running commands on the shell. I did this because the Next.js project is in that directory, where the <code>package.json</code> is located.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Previous code</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">Deploy-Production:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">defaults:</span>
      <span class="hljs-attr">run:</span>
        <span class="hljs-attr">working-directory:</span> <span class="hljs-number">01</span><span class="hljs-string">-simple-blog</span>
<span class="hljs-comment"># Other code</span>
</code></pre>
<p>Then the steps involved:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Previous code</span>
 <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Vercel</span> <span class="hljs-string">CLI</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">--global</span> <span class="hljs-string">vercel@latest</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Pull</span> <span class="hljs-string">Vercel</span> <span class="hljs-string">Environment</span> <span class="hljs-string">Information</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">pull</span> <span class="hljs-string">--yes</span> <span class="hljs-string">--environment=production</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">Project</span> <span class="hljs-string">Artifacts</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">build</span> <span class="hljs-string">--prod</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">continue-on-error:</span> <span class="hljs-literal">true</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Project</span> <span class="hljs-string">Artifacts</span> <span class="hljs-string">to</span> <span class="hljs-string">Vercel</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">vercel</span> <span class="hljs-string">deploy</span> <span class="hljs-string">--prebuilt</span> <span class="hljs-string">--prod</span> <span class="hljs-string">--token=${{</span> <span class="hljs-string">secrets.VERCEL_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<p>With these steps, Vercel is first installed on the GitHub runner. Then the vercel environment information is pulled. The project is built with <code>vercel build</code>, and the pre-built artifacts are then pushed to Vercel.</p>
<h3 id="heading-push-to-github-and-watch-your-code-deploy">Push to GitHub and watch your code deploy</h3>
<p>Stage your changes, if any:</p>
<pre><code class="lang-bash">git add .
</code></pre>
<p>Commit the changes:</p>
<pre><code class="lang-bash">git commit -m <span class="hljs-string">"Added GitHub Actions workflow"</span>
</code></pre>
<p>And push:</p>
<pre><code class="lang-bash">git push origin
</code></pre>
<p>Now, go to your repository online, and check the deployment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749042451313/3fa5a9a8-c14b-4f55-9e04-c51310500c3f.png" alt="3fa5a9a8-c14b-4f55-9e04-c51310500c3f" class="image--center mx-auto" width="899" height="46" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749042384631/d941710c-697d-46b8-a106-30f6ac3cedc3.png" alt="Workflow run logs" class="image--center mx-auto" width="981" height="492" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With your basic GitHub workflow in place, you can now make changes to your code, push to GitHub, and have it deploy automatically. Though Vercel allows you to connect your repository directly, this method provides you with more flexibility and customizability. If you enjoyed this article, share it with others. You can also reach me on <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or <a target="_blank" href="https://x.com/chidiadi01">X</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How To Build A Simple Portfolio Blog With Next.js ]]>
                </title>
                <description>
                    <![CDATA[ I have written articles on different platforms including LinkedIn, The Network Bits (Substack), and freeCodeCamp. So I wanted to bring all of these articles together in a single place where someone could go and see all my work. A blog sounded like a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-simple-portfolio-blog-with-nextjs/</link>
                <guid isPermaLink="false">6839c439ebab1f7974eac355</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Fri, 30 May 2025 14:44:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747235586248/7424bce0-24da-4f70-a5aa-31249d799094.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I have written articles on different platforms including LinkedIn, The Network Bits (Substack), and freeCodeCamp. So I wanted to bring all of these articles together in a single place where someone could go and see all my work.</p>
<p>A blog sounded like a good solution for this, so I set out to build one. In this article, I will walk you through how I did it with Next.js.</p>
<p>The basic idea here was to build a website where I wouldn’t need to write code in the future. I just wanted to be able to add the URL of a new article to a JSON file, and the website would extract information like the title, date, cover image, and description and then update itself with it. No database.</p>
<p>To understand how I would go about it, I checked the metadata of the HTML text from each of the platforms I considered. I used my articles, of course, like the one in the project folder. I found out that most of them used Open Graph metadata. So, that was easy to scrape. But, I also found out that some information wasn’t in the meta tags – instead, it was in the JSON-LD. At the end of the day, I ended up using both in my functions.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-the-blog-site-works">How the Blog Site Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-does-an-article-look-like">The Structure of an Article on the Blog</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-the-search-feature-work">How the Search Feature Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-structure">The Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-steps-to-build-the-blog">Steps to Build the Blog</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<p>Understanding this article requires some knowledge of programming and web development. You need to have basic knowledge of HTTP, HTML, CSS, JavaScript, and React to be able to follow along easily.</p>
<p>If you don't have those skills, you may still be able to understand the general structure and working principles.</p>
<h2 id="heading-how-the-blog-site-works">How the Blog Site Works</h2>
<p>The project consists of client components and server components. It is a website, so ideally, it's just a front-end. But it has to fetch data from URLs – and doing that from the client-side won’t work due to CORS blocking, as the requests will be emanating from a browser. So, it has to run on the server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748524048812/76baf11d-a80a-4d07-beba-065c74536541.png" alt="Home page calling the fetch articles function." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The <code>fetchArticles()</code> function runs on the server – then this happens:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748536836320/9813a669-ac07-480a-8270-6f2f36ceda22.png" alt="Rough flowchart showing what happens in the fetchArticles function" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The <code>fetchArticles()</code> function accesses the URLs, extracts and processes the HTML and JSON Linked Data objects from the response, and returns an array of Article objects to the Home page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748536952680/35fec49a-6c44-4978-9acb-755eb5ed810f.png" alt="fetch articles function gets called and returns an array of Article objects" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The <code>HomePage</code> component is a client side component that has another component in it, named <code>HomeClient</code>. This <code>HomeClient</code> is a client side component. It has to be because it has useState hooks.</p>
<p>But the <code>HomePage</code> component calls the <code>fetchArticles()</code> function and sets the <code>articles</code> constant (which is an array of <code>Article</code> objects, as defined by the interface in the <code>ArticleCard.tsx</code> file). The <code>articles</code> constant is then passed down to the <code>HomeClient</code> component as a prop.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881308147/1fedcbb0-f9d4-47dd-afea-b7f231595a58.png" alt="The HomePage component, and its child component, HomeClient." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Inside the <code>HomeClient</code> component, there are two components – the <code>Hero</code> component, and the <code>MainBody</code> component. The Hero component shows the welcome message, and also has the search bar. The MainBody component is where the tags and the article grid are. Logic for filtering articles are also in the MainBody component.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881333468/267d0158-df41-4a1d-8098-e3219fe7db4d.png" alt="The Hero and MainBody components inside the HomeClient component." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Inside the MainBody component, there is the <code>ArticleCard</code> component that takes the filtered array of Article objects from the MainBody as props, and renders an article card for each. These cards are rendered inside the grid in the MainBody component.</p>
<h3 id="heading-what-does-an-article-look-like">What does an article look like?</h3>
<p>The articles are defined by an interface:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Article {
  id: <span class="hljs-built_in">number</span>;
  title?: <span class="hljs-built_in">string</span>;
  description?: <span class="hljs-built_in">string</span>;
  publishedDate?: <span class="hljs-built_in">string</span>;
  url: <span class="hljs-built_in">string</span>;
  imgUrl?: <span class="hljs-built_in">string</span>;
  siteName?: <span class="hljs-built_in">string</span>;
  tags?: <span class="hljs-built_in">string</span>[];
}
</code></pre>
<p>The interface, as shown above, specifies that the object will have eight properties, of which only the <code>id</code> and <code>url</code> are compulsory. Those compulsory properties are actually what’s needed in the JSON file from which the web server will read.</p>
<p>When the URL is visited by the server, the title, description, and other properties (except the tags) are obtained automatically and populated. Then the object is created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881670519/a2cbdbbf-6cfd-40e7-91e5-5da711198dc7.png" alt="Article card renderings" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The article cards consist of the article’s cover image, the name of the platform where it was published, the date published, the title, and a description. All of this is wrapped in an anchor linking to the URL. The tags are not visible on the cards, but are used in filtering operations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881715907/7ab4ad87-7da6-44e2-8329-35bcb999b825.png" alt="Array of Article objects in browser console." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-does-the-search-feature-work">How does the search feature work?</h3>
<p>There's a reason why the Hero component and the MainBody component are in the same parent component. That wasn't my initial design, but after I saw that the search bar would look better in the Hero component, and that I needed to set the <code>searchTerm</code> state in the Hero component and use it in the MainBody component, that became the best option for me: to put both of them in the same parent, so I could pass down the useState hook as props into both of them.</p>
<p>The search feature works basically by filtering the <code>articles</code> array based on the tags selected, or the search term entered. Here is what the code looks like:</p>
<pre><code class="lang-typescript"> useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> anyTagActive = isActive.some(<span class="hljs-function">(<span class="hljs-params">val</span>) =&gt;</span> val);

    <span class="hljs-keyword">const</span> filtered = articles.filter(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Search term: '</span> + searchTerm || <span class="hljs-string">'searchTerm'</span>);
      <span class="hljs-keyword">const</span> searchMatch =
        article.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> tag.toLowerCase().includes(searchTerm.toLowerCase())) ||
        article.siteName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.publishedDate?.toLowerCase().includes(searchTerm.toLowerCase());


        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'This is the searchMarch: '</span> + searchMatch || <span class="hljs-string">'FALSE searchMatch'</span>);
        <span class="hljs-built_in">console</span>.log(article.title || <span class="hljs-string">'article.title no wan show'</span>);

      <span class="hljs-keyword">const</span> tagMatch = article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> index = tags.indexOf(tag);
        <span class="hljs-keyword">return</span> index !== <span class="hljs-number">-1</span> &amp;&amp; isActive[index];
      }) || <span class="hljs-literal">false</span>;

      <span class="hljs-keyword">if</span> (anyTagActive) {
        <span class="hljs-keyword">return</span> tagMatch &amp;&amp; searchMatch; <span class="hljs-comment">// Only return articles if tag is active and search matches</span>
      }

      <span class="hljs-keyword">return</span> searchMatch; <span class="hljs-comment">// If no tags active, return all that match the search term</span>
    });

    setFilteredArticles(filtered);
  }, [articles, searchTerm, isActive]);
</code></pre>
<p>Here, we use a <code>useEffect()</code> hook to monitor for changes in the <code>articles</code>, <code>searchTerm</code>, and <code>isActive</code> constants. <code>isActive</code> is a <code>useState()</code> hook that has an array of boolean values the length of the tags array.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));
</code></pre>
<p>Here, the <code>filtered</code> constant is equal to the filtered values of <code>articles</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> filtered = articles.filter();
</code></pre>
<p>Inside the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter">filter method</a> is where the arrow function with the logic for filtering is written – <code>(article) =&gt; {//logic}</code>. We have two constants: <code>tagMatch</code> and <code>searchMatch</code>. The <code>searchMatch</code> constant is true when the title, description, tags, site name, or published date includes the search term. Else, it's false. The <code>tagMatch</code> constant is true when any tag from the article's array of tag is present in the tag list, and also has a corresponding <code>isActive</code> value of true.</p>
<p>If any tag at all is active, then the results for both <code>tagMatch</code> and <code>searchMatch</code> are returned, but if no tag at all is active, then only the <code>searchMatch</code> is returned as true.</p>
<p>The filtered article list is what is then passed into the <code>ArticleCard</code> component.</p>
<pre><code class="lang-typescript">&lt;ArticleCard articles={filteredArticles} /&gt;
</code></pre>
<h2 id="heading-project-structure">Project Structure</h2>
<p>This is what the project file structure looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748605144895/e56eea92-8851-4717-ae82-0e15f70dc31f.png" alt="e56eea92-8851-4717-ae82-0e15f70dc31f" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>At the root, we have the config files and <code>node_modules</code> which is not displayed here. The <code>public</code> folder holds all the images and icons. Then, in the <code>src</code> folder, we have <code>app</code>, <code>component</code>, and <code>utils</code>.</p>
<p>The <code>components</code> folder holds the files for the components – the nav bar, footer, hero, main body and article card. The <code>utils</code> folder has all the functions that run in the background and do not need to render anything. The <code>fetchArticles</code> function is there, along with other functions for extracting the date published, title, description, image URL, and others from HTTP responses gotten from the article URLs. The <code>app</code> folder has the favicon, the global CSS stylesheet, the <code>page</code> and <code>layout</code> files, <code>articles.json</code> which is the JSON file where I add new article URLs for rendering, a test HTML file (wsl.html), and the <code>about/</code> and <code>api/</code> directories.</p>
<p>Inside the about folder, we have the about page, and inside the API folder, we have tthe folder, <code>metadata-local-test</code> which is no longer relevant to the project. I used it initially to create an internal API to fetch from the URLs. But I later restructured the codebase.</p>
<h2 id="heading-steps-to-build-the-blog">Steps to Build the Blog</h2>
<h3 id="heading-1-install-nextjs">1. Install Next.js</h3>
<p>To install Next.js, navigate to the folder where you want the project to reside and open that location in your terminal. Then type the following:</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>You're going to be met with the following prompts:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881930473/228a02f7-6571-46f0-9503-d4606e19bd10.png" alt="Installing Next.js" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-2-navigate-to-your-newly-created-project-folder-and-install-dependencies">2. Navigate to your newly created project folder and install dependencies</h3>
<p>In the newly created project folder, run the project in development mode to preview your newly created Next project. You will be shown a message directing you to localhost on port 3000. Now, it's time for us to start creating what we want.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882046144/df46e975-14f2-4dc9-81d7-5bbb9a344f7b.png" alt="Success! Navigating to the project directory." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, one more thing you'll need to do. In the project, I used lucide-react to get one of the icons, and cheerio to extract data from the HTML. So, you’ll need to install those dependencies.</p>
<p>To install lucide-react, use this command in the project folder:</p>
<pre><code class="lang-bash">npm install lucide-react
</code></pre>
<p>Then install cheerio:</p>
<pre><code class="lang-bash">npm install cheerio
</code></pre>
<h3 id="heading-3-change-the-title-and-description-in-the-page-metadata">3. Change the title and description in the page metadata</h3>
<p>The title is what shows up at the top of your browser tab when you open up the website. Right now, it should be showing 'Create Next App.' We don't want that.</p>
<p>Since this is not just HTML, there is no <code>index.html</code> to change the title in the header element. Rather, Next.js provides us a <code>Metadata</code> object we can use to change things like that. And it'll be in the <code>layout.tsx</code> file in the <code>app</code> or <code>src</code> folder. Head over there and change it to whatever you want the title to be. I’m using “Chidiadi Portfolio Blog”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882205569/b652a7de-00b7-4f0d-943c-4f80a62a7f91.png" alt="Changing metadata" width="600" height="400" loading="lazy"></p>
<h3 id="heading-4-create-the-necessary-components">4. Create the necessary components</h3>
<p>Navigate to the side panel, and under the <code>src</code> folder, create a components folder. This is where the components will live. Here, create the article card, footer, main body and nav bar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882335350/69d1f7ed-ea67-47e7-8b0a-e25429190a3e.png" alt="Components folder" width="600" height="400" loading="lazy"></p>
<p>For the <strong>Navbar</strong>, this is the code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Navbar</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span>(
        &lt;&gt;
        &lt;div className=<span class="hljs-string">"text-3xl md:text-base flex w-[100vw] md:w-[98.2vw] lg:w-[98.8vw] h-[60px] bg-black text-white px-0 md:px-7 md:py-2 items-center justify-center md:justify-between"</span>&gt;
            &lt;h1 className=<span class="hljs-string">"font-bold"</span>&gt;CHIDIADI   ANYANWU&lt;/h1&gt;
            &lt;div className=<span class="hljs-string">"hidden md:block flex space-x-4"</span>&gt;
                &lt;a href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"hover:text-gray-400"</span>&gt;Blog&lt;/a&gt;
                &lt;a href=<span class="hljs-string">"/about"</span> className=<span class="hljs-string">"hover:text-gray-400"</span>&gt;About&lt;/a&gt;    
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>Here's what the <strong>Hero</strong> component looks like:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { Search } <span class="hljs-keyword">from</span> <span class="hljs-string">'lucide-react'</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">interface</span> HeroProps {
    searchTerm: <span class="hljs-built_in">string</span>;
    setSearchTerm: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
  }
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Hero</span>(<span class="hljs-params">{ searchTerm, setSearchTerm }: HeroProps</span>) </span>{
    <span class="hljs-keyword">const</span> [buttonColor, setButtonColor] = useState(<span class="hljs-string">''</span>);

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"bg-[url('/img-one-1.jpg')] bg-cover bg-center bg-no-repeat flex flex-col items-center justify-center h-[400px] relative"</span>&gt;
           &lt;div className=<span class="hljs-string">" absolute inset-0 bg-black opacity-60"</span>&gt;&lt;/div&gt; 
            &lt;h1 className=<span class="hljs-string">"text-4xl text-white font-bold text-center z-10"</span>&gt;My Portfolio Blog&lt;/h1&gt;
            &lt;p className=<span class="hljs-string">"mt-4 mx-4 text-xlarge text-white md:text-xl text-justify md:text-center z-10"</span> style={{ fontFamily: <span class="hljs-string">"Cormorant Garamond"</span> }}&gt;
                My name is Chidiadi Anyanwu. I am a technical writer <span class="hljs-keyword">with</span> a strong background <span class="hljs-keyword">in</span> networking. 
                I write about Networking, Cloud, DevOps, and even sometimes web development like <span class="hljs-built_in">this</span> one. I built <span class="hljs-built_in">this</span>
                website <span class="hljs-keyword">with</span> Next.js, and there<span class="hljs-string">'s also an &lt;a href="/" className="text-blue-500 hover:text-blue-700 hover:underline"&gt;article about that.&lt;/a&gt;
                  This website holds my technical articles in one place. It is a repository of my written works.
            &lt;/p&gt;
            &lt;div id="searchbar" className="h-9xl mt-4 flex align-items-center justify-center w-full" &gt;

                &lt;form onSubmit={(e) =&gt; {e.preventDefault();  setSearchTerm(searchTerm);}} className="group mt-4 relative w-[70%] md:w-[50%]"&gt;
                    &lt;input  value={searchTerm} onChange={(e) =&gt; setSearchTerm(e.target.value) } onFocus={()=&gt;{setButtonColor('</span>bg-blue<span class="hljs-number">-500</span><span class="hljs-string">'); console.log('</span>input focused<span class="hljs-string">')}} onBlur={()=&gt;{setButtonColor('</span><span class="hljs-string">');}}type="search" placeholder="Search Chidiadi'</span>s articles<span class="hljs-string">" className="</span>h-[<span class="hljs-number">50</span>px] w-full px-[<span class="hljs-number">48</span>px] border<span class="hljs-number">-3</span> border-blue<span class="hljs-number">-300</span> rounded-[<span class="hljs-number">25</span>px] focus:outline-none focus:border-blue<span class="hljs-number">-500</span> text-black bg-white<span class="hljs-string">"/&gt;
                    &lt;button className={`h-[42px] w-[42px] absolute right-0 mr-1.5 mt-1 rounded-[50%] bg-blue-300 ${buttonColor}`}&gt;
                        &lt;Search  className='m-auto text-white'/&gt;
                    &lt;/button&gt;
                &lt;/form&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    );
}</span>
</code></pre>
<p>In this file, we created the HeroProps interface to accept the search props. Then we deconstructed both <code>searchTerm</code> and <code>setSearchTerm</code> from it as props to the Hero component. We’ll make it a client component <code>'use client'</code> because of the buttonColor <code>useState()</code> hook that changes when the search bar is clicked and sets the search button background color.</p>
<p>The <strong>MainBody</strong> component looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> ArticleCard, { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'./ArticleCard'</span>;

<span class="hljs-keyword">interface</span> MainBodyProps {
  searchTerm: <span class="hljs-built_in">string</span>;
  articles: Article[];
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MainBody</span>(<span class="hljs-params">{ searchTerm, articles }: MainBodyProps</span>) </span>{
  <span class="hljs-comment">// Get articles from JSON file and create array of article objects</span>

  <span class="hljs-keyword">const</span> [filteredArticles, setFilteredArticles] = useState&lt;Article[]&gt;([]);

  <span class="hljs-keyword">const</span> tags = [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cloud"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"Cybersecurity"</span>];
  <span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));


  <span class="hljs-comment">// Filter articles based on search term and active tags</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> anyTagActive = isActive.some(<span class="hljs-function">(<span class="hljs-params">val</span>) =&gt;</span> val);

    <span class="hljs-keyword">const</span> filtered = articles.filter(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Search term: '</span> + searchTerm || <span class="hljs-string">'searchTerm'</span>);
      <span class="hljs-keyword">const</span> searchMatch =
        article.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> tag.toLowerCase().includes(searchTerm.toLowerCase())) ||
        article.siteName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.publishedDate?.toLowerCase().includes(searchTerm.toLowerCase());


        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'This is the searchMarch: '</span> + searchMatch || <span class="hljs-string">'FALSE searchMatch'</span>);
        <span class="hljs-built_in">console</span>.log(article.title || <span class="hljs-string">'article.title no wan show'</span>);

      <span class="hljs-keyword">const</span> tagMatch = article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> index = tags.indexOf(tag);
        <span class="hljs-keyword">return</span> index !== <span class="hljs-number">-1</span> &amp;&amp; isActive[index];
      }) || <span class="hljs-literal">false</span>;

      <span class="hljs-keyword">if</span> (anyTagActive) {
        <span class="hljs-keyword">return</span> tagMatch &amp;&amp; searchMatch; <span class="hljs-comment">// Only return articles if tag is active and search matches</span>
      }

      <span class="hljs-keyword">return</span> searchMatch; <span class="hljs-comment">// If no tags active, return all that match the search term</span>
    });

    setFilteredArticles(filtered);
  }, [articles, searchTerm, isActive]); 

  <span class="hljs-built_in">console</span>.log(filteredArticles);

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">'scroll-smooth'</span>&gt;
      &lt;div id=<span class="hljs-string">"tags"</span> className=<span class="hljs-string">"flex w-full h-[200px] md:h-[60px] justify-center gap-5 py-4 flex-wrap max-w-[100vw] scroll-smooth"</span>&gt;
        {tags.map(<span class="hljs-function">(<span class="hljs-params">tag, index</span>) =&gt;</span> (
          &lt;p
            key={index}
            onClick={<span class="hljs-function">() =&gt;</span> {
              <span class="hljs-keyword">const</span> newIsActive = [...isActive];
              newIsActive[index] = !newIsActive[index];
              setIsActive(newIsActive);
            }}
            className={<span class="hljs-string">`h-[48px] w-[140px] border-3 rounded-[40px] px-2 py-2 text-center font-bold <span class="hljs-subst">${
              isActive[index]
                ? <span class="hljs-string">'bg-black border-black text-white hover:bg-gray-700 hover:border-gray-700'</span>
                : <span class="hljs-string">'border-blue-500 hover:bg-blue-500 hover:text-white'</span>
            }</span>`</span>}&gt;
            {tag}
          &lt;/p&gt;
        ))}
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">"articlegrid"</span> className=<span class="hljs-string">"w-[100vw] md:w-[98vw] grid gap-2 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 mt-5 px-3 py-3"</span>&gt;
        &lt;ArticleCard articles={filteredArticles} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Here, we have props from the parent component too, but we only need the articles fetched and the search term. We don't need to set the fetch term from this component.</p>
<p>To render the tags, I first created the array of tags and an array of boolean values to record the states of the tags (whether they're active or inactive).</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> tags = [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cloud"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"Cybersecurity"</span>];
<span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));
</code></pre>
<p>Then, inside the return statement, I mapped through the tag array to render them one by one. The onClick event handler also works here to make sure that the <code>isActive</code> state for that particular tag is toggled when it is clicked.</p>
<p>So how does this work? It creates a new array called <code>newIsActive</code> that is a copy of the <code>isActive</code> array. It then gets the particular tag by index number and inverts it. Then it sets the <code>isActive</code> array to this new array.</p>
<pre><code class="lang-typescript">{tags.map(<span class="hljs-function">(<span class="hljs-params">tag, index</span>) =&gt;</span> (
          &lt;p
            key={index}
            onClick={<span class="hljs-function">() =&gt;</span> {
              <span class="hljs-keyword">const</span> newIsActive = [...isActive];
              newIsActive[index] = !newIsActive[index];
              setIsActive(newIsActive);
            }} . . .
</code></pre>
<p>This is the code for the <strong>ArticleCard</strong>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Image  <span class="hljs-keyword">from</span> <span class="hljs-string">'next/image'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Article {
  id: <span class="hljs-built_in">number</span>;
  title?: <span class="hljs-built_in">string</span>;
  description?: <span class="hljs-built_in">string</span>;
  publishedDate?: <span class="hljs-built_in">string</span>;
  url: <span class="hljs-built_in">string</span>;
  imgUrl?: <span class="hljs-built_in">string</span>;
  siteName?: <span class="hljs-built_in">string</span>;
  tags?: <span class="hljs-built_in">string</span>[];
}

<span class="hljs-keyword">interface</span> ArticleProps {
  articles: Article[];
}

<span class="hljs-keyword">const</span> ArticleCard = <span class="hljs-function">(<span class="hljs-params">{ articles }: ArticleProps</span>) =&gt;</span> {

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
    {articles ?

      (articles.map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        <span class="hljs-comment">//anchor tag for the link</span>
        &lt;a key={id}  href={item.url} className=<span class="hljs-string">'max-w-[350px] mx-auto mb-5'</span>&gt;
            &lt;div className=<span class="hljs-string">"sm:w-[350px] hover:brightness-70"</span> data-title={item.title} data-description={item.description} data-published-date={item.publishedDate} data-tag=<span class="hljs-string">"Networking"</span> data-site-name={item.siteName}&gt;
            &lt;Image
              src={item.imgUrl || <span class="hljs-string">'/img-2.jpg'</span>} 
              alt={item.title || <span class="hljs-string">'Article Image'</span>}
              width={<span class="hljs-number">350</span>}
              height={<span class="hljs-number">400</span>}
              className=<span class="hljs-string">"object-cover rounded-[10px]"</span>
            /&gt;
            &lt;div className=<span class="hljs-string">"flex h-[43px] text-[14px] text-gray-500 gap-2"</span>&gt;
                &lt;p id=<span class="hljs-string">"Platform"</span> className=<span class="hljs-string">"py-2 h-[42px] md:text-sm mt-auto mb-auto"</span>&gt;{item.siteName}&lt;/p&gt;
                &lt;div className=<span class="hljs-string">"h-1 w-1 bg-black rounded-full mt-auto mb-auto bg-gray-500"</span>&gt;&lt;/div&gt;
                &lt;p id=<span class="hljs-string">"publishedDate"</span> className=<span class="hljs-string">"py-2 h-[42px] mt-auto mb-auto"</span>&gt;{item.publishedDate}&lt;/p&gt;
            &lt;/div&gt;
            &lt;h1 id=<span class="hljs-string">"titleOfArticle"</span> className=<span class="hljs-string">"font-bold text-base md:text-3xl"</span>&gt;{item.title}&lt;/h1&gt;
            &lt;br/&gt;
            &lt;p className=<span class="hljs-string">'w-full md:w-[350px]'</span>&gt;{item.description}&lt;/p&gt;
            &lt;/div&gt;
        &lt;/a&gt;
      )))
      :

      ( <span class="hljs-built_in">Array</span>(<span class="hljs-number">6</span>).fill(<span class="hljs-number">0</span>).map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        &lt;div key={id} className=<span class="hljs-string">"w-full md:w-[350px] h-[350px] bg-gray-500 mx-auto mb-5 hover:brightness-80 rounded-[10px] animate-pulse"</span>&gt;&lt;/div&gt;
      )))
    }
    &lt;/&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ArticleCard;
</code></pre>
<p>Here, we defined and exported the <code>Article</code> interface so that we can create <code>Article</code> objects in the <code>MainBody</code>. Then, we created an interface to pass down the props of an array of <code>Article</code> objects.</p>
<p>Next, there's this part to ensure it renders something even if for some reason no Article object was passed:</p>
<pre><code class="lang-typescript">{
    article?
    ( {<span class="hljs-comment">/*If article exists, render this*/</span>} )
    :
    ( {<span class="hljs-comment">/*Else, render this */</span>} )
}
</code></pre>
<p>Our fail-safe here is an empty array of six objects with the Tailwind <code>animate-pulse</code>:</p>
<pre><code class="lang-typescript"> ( <span class="hljs-built_in">Array</span>(<span class="hljs-number">6</span>).fill(<span class="hljs-number">0</span>).map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        &lt;div key={id} className=<span class="hljs-string">"w-full md:w-[350px] h-[350px] bg-gray-500 mx-auto mb-5 hover:brightness-80 rounded-[10px] animate-pulse"</span>&gt;&lt;/div&gt;
      )))
</code></pre>
<p>I could have made this part much better, but I was feeling a little lazy. I also used the <code>Image</code> from Next, instead of the regular <code>img</code>. This requires that you edit the <code>next.config.ts</code> file. I had to go add all the paths that the images could possibly be loaded from:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882422600/e3ad4762-1199-4276-a524-d27519a37c52.png" alt="next.config.ts" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Just like in the screenshot above, the syntax is:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { NextConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">const</span> nextConfig: NextConfig = {
  images: {
    remotePatterns: [
        {
            protocol:<span class="hljs-string">"https"</span>,
            hostname:<span class="hljs-string">"licdn.com"</span>,
            pathname:<span class="hljs-string">"/**"</span>
        },
        {
            protocol:<span class="hljs-string">""</span>,
            hostname:<span class="hljs-string">""</span>,
            pathname:<span class="hljs-string">""</span>
        }
        ],
    },
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> nextConfig;
</code></pre>
<p>It takes a <code>remotePatterns</code> array that consists of remote pattern objects, which have a protocol, hostname, and pathname property. Make sure the protocol and hostname properties are not empty like in the second object in the code sample above. That would cause errors. It’s either the objects are populated properly or they’re deleted.</p>
<p>The <strong>Footer</strong> looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Footer</span>(<span class="hljs-params"></span>) </span>{

    <span class="hljs-keyword">return</span> (
        &lt;footer className=<span class="hljs-string">"bg-gray-100 text-center py-4 mt-10"</span>&gt;
            &lt;div className=<span class="hljs-string">"flex align-items-center justify-center text-sm text-blue-400 font-bold"</span>&gt;
                &lt;a href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"hover:text-blue-600"</span>&gt;Home&lt;/a&gt;
                &lt;p&gt; &amp;nbsp; &amp;nbsp; | &amp;nbsp; &amp;nbsp; &lt;/p&gt;
                &lt;a href=<span class="hljs-string">"/about"</span> className=<span class="hljs-string">"hover:text-blue-600"</span>&gt;About&lt;/a&gt;
            &lt;/div&gt;
            &lt;p className=<span class="hljs-string">"text-sm text-gray-600"</span>&gt;© {<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()} Chidiadi Anyanwu. All rights reserved.&lt;/p&gt;
            &lt;p className=<span class="hljs-string">"text-sm text-gray-600"</span>&gt;Built <span class="hljs-keyword">with</span> Next.js and Tailwind CSS&lt;/p&gt;
        &lt;/footer&gt;
    );

}
</code></pre>
<p>This <code>new Date().getFullYear()</code> helps me get the current year all the time.</p>
<h3 id="heading-5-place-the-components-properly">5. Place the components properly</h3>
<p>The nav bar and footer components are things that will not change no matter the page you visit. So they should be placed in a more permanent and untouched location. We can put both of them in the root <code>layout.tsx</code> file like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882493175/3d7381ca-48c6-43c1-becc-8692c6b090c4.png" alt="layout.tsx file" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<pre><code class="lang-typescript"> &lt;body className={<span class="hljs-string">`<span class="hljs-subst">${geistSans.variable}</span> <span class="hljs-subst">${geistMono.variable}</span> antialiased scroll-smooth`</span>}&gt;
      &lt;Navbar /&gt;
      {children}
      &lt;Footer /&gt;
  &lt;/body&gt;
</code></pre>
<p><code>{children}</code> is where the contents from <code>page.tsx</code> will enter. So, we sandwiched all the other content in the Nav bar and footer. Apart from adding <code>&lt;link /&gt;</code> tags for fonts (because this is where the root HTML is), we really don't have business with this file again.</p>
<p>Now, in the same <code>app/</code> folder where this layout file is, create the <code>&lt;HomeClient /&gt;</code> file. This is how it looks:</p>
<pre><code class="lang-typescript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Hero <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/hero'</span>;
<span class="hljs-keyword">import</span> MainBody <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/mainbody'</span>;
<span class="hljs-keyword">import</span> { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/ArticleCard'</span>;

<span class="hljs-keyword">interface</span> Props {
  initialArticles: Article[];
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomeClient</span>(<span class="hljs-params">{ initialArticles }: Props</span>) </span>{
  <span class="hljs-keyword">const</span> [searchTerm, setSearchTerm] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [articles, setArticles] = useState&lt;Article[]&gt;(initialArticles);

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;Hero searchTerm={searchTerm} setSearchTerm={setSearchTerm} /&gt;
      &lt;MainBody searchTerm={searchTerm} articles={articles} /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Then, put the <code>HomeClient</code> component inside the <code>page.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { fetchArticles } <span class="hljs-keyword">from</span> <span class="hljs-string">'../utils/fetchArticles'</span>;
<span class="hljs-keyword">import</span> HomeClient <span class="hljs-keyword">from</span> <span class="hljs-string">'./HomeClient'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> revalidate = <span class="hljs-number">3600</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomePage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> fetchArticles(); 

  <span class="hljs-keyword">return</span> &lt;HomeClient initialArticles={articles} /&gt;;
}
</code></pre>
<p>The server is set to fetch the articles at build time, and fetch again (revalidate) every hour (3600s). So, it doesn't fetch the articles from the URLs upon user request of the page.</p>
<p>Initially, it worked by fetching any time the component was mounted, but I noticed that this caused the page to load very slowly. The articles didn't pop-up on time, because there's a lot of fetching to be done.</p>
<p>In that same <code>app/</code> directory, create an <code>about/</code> folder, and create the <code>page.tsx</code> for that route:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;div className=<span class="hljs-string">"flex items-center justify-center"</span>&gt;
                &lt;div className=<span class="hljs-string">"margin-auto w-[90vw] md:w-[60vw] lg:w-[50vw] h-[450px] hover:bg-gray-100 border-1 md:border-2 border-gray-200 shadow-sm flex flex-wrap  items-center justify-center gap-2 mt-10 mb-10 rounded-lg"</span>&gt;
                    &lt;Image
                        src=<span class="hljs-string">"/MyPhotoChidiadi.jpg"</span> 
                        alt=<span class="hljs-string">"Avatar"</span>
                        className=<span class="hljs-string">"rounded-[50%] h-30 w-30"</span>
                        width={<span class="hljs-number">120</span>} 
                        height={<span class="hljs-number">120</span>} 
                    /&gt;
                    &lt;div className=<span class="hljs-string">"w-[90%] mx-auto"</span>&gt;
                        &lt;h1 className=<span class="hljs-string">"text-xl text-center my-1 font-bold"</span>&gt;About Me&lt;/h1&gt;
                        &lt;p className=<span class="hljs-string">"text-justify my-3"</span>&gt;
                            My name is Chidiadi Anyanwu. I love breaking down complex concepts.
                            I write about Networking, Cloud, DevOps, and even sometimes web development. 
                            You can connect <span class="hljs-keyword">with</span> me by following <span class="hljs-built_in">any</span> <span class="hljs-keyword">of</span> the links below.
                        &lt;/p&gt;
                        &lt;hr className=<span class="hljs-string">"border-gray-300 my-3"</span> /&gt;
                        &lt;div className=<span class="hljs-string">"flex gap-7 w-full my-3 justify-center"</span>&gt; 
                            &lt;a href=<span class="hljs-string">"https://github.com/chidiadi01"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'/github-icon.svg'</span> alt=<span class="hljs-string">"github logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>} /&gt;
                            &lt;/a&gt;
                            &lt;a href=<span class="hljs-string">"https://linkedin.com/in/chidiadi-anyanwu"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'linkedin-icon.svg'</span> alt=<span class="hljs-string">"linkedin logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>}/&gt;
                            &lt;/a&gt;
                            &lt;a href=<span class="hljs-string">"https://x.com/chidiadi01"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'x-2.svg'</span> alt=<span class="hljs-string">"x logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>}/&gt; 
                            &lt;/a&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/&gt;
    );
}
</code></pre>
<h3 id="heading-6-create-the-utils-folder-and-all-the-functions">6. Create the utils folder and all the functions</h3>
<p>The next step is to create all these files.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947058303/01b6fd3d-3666-46fe-8928-1ad5b1532625.png" alt="utils folder" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Under the same <code>app/</code> directory, create the <code>utils/</code> folder. <code>app/utils/</code>. Then start with the <code>fetchArticles()</code> function. The <code>fetchArticles()</code> function is what accesses the API route in the project to obtain the array of Article objects from an array of URLs. The <code>fetchArticles()</code> function returns an array of those objects which are then stored in the <code>articles</code> variable. It looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getPublishedDate } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getPublishedDate'</span>;
<span class="hljs-keyword">import</span> { getTitle } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getTitle'</span>;
<span class="hljs-keyword">import</span> { getImageURL } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getImageURL'</span>;
<span class="hljs-keyword">import</span> { getDescription } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getDescription'</span>;
<span class="hljs-keyword">import</span> { getPlatform } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getPlatform'</span>;
<span class="hljs-keyword">import</span> articleFile <span class="hljs-keyword">from</span> <span class="hljs-string">'../app/articles.json'</span>;
<span class="hljs-keyword">import</span> { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/ArticleCard'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchArticles</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Article</span>[]&gt; </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetching articles...'</span>);
  <span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(
    articleFile.articles.map(<span class="hljs-keyword">async</span> (item) =&gt; {
      <span class="hljs-comment">//Validate URL first</span>
       <span class="hljs-keyword">if</span> (!item.url || <span class="hljs-keyword">typeof</span> item.url !== <span class="hljs-string">'string'</span> || item.url.trim() === <span class="hljs-string">''</span>) {
        <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">`Invalid URL: <span class="hljs-subst">${item.url}</span>`</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">// Skip this item</span>
      } 
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The URL: '</span> + item.url);
      <span class="hljs-keyword">let</span> data;
      <span class="hljs-keyword">try</span> {

        <span class="hljs-comment">// Fetch metadata and HTML from the URL</span>
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(item.url, {
          headers: {
            <span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'</span>,
            <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'</span>,
            <span class="hljs-string">'Accept-Language'</span>: <span class="hljs-string">'en-US,en;q=0.5'</span>,
            <span class="hljs-string">'Referer'</span>: <span class="hljs-string">'https://www.google.com/'</span>, 
          },
        });

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetched: '</span>+ item.url);

        <span class="hljs-keyword">if</span> (!response.ok) {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`HTTP error! Status: <span class="hljs-subst">${response.status}</span> for URL: <span class="hljs-subst">${item.url}</span>`</span>);
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`HTTP error! Status: <span class="hljs-subst">${response.status}</span>`</span>);
        }

        <span class="hljs-keyword">const</span> html = <span class="hljs-keyword">await</span> response.text();
        <span class="hljs-keyword">const</span> $ = cheerio.load(html);
        <span class="hljs-keyword">const</span> jsonScript = $(<span class="hljs-string">'script[type="application/ld+json"]'</span>).html();

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Gotten HTML response'</span>);

        <span class="hljs-keyword">if</span> (!jsonScript) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'No JSON-LD script found on page'</span>);
        }

        <span class="hljs-keyword">const</span> metadata = <span class="hljs-built_in">JSON</span>.parse(jsonScript);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Gotten metadata'</span>);


        <span class="hljs-comment">// Combine metadata and HTML into a single object</span>
        data = { metadata, html };
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Failed to fetch metadata for URL: <span class="hljs-subst">${item.url}</span>`</span>, error);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The default empty object has been returned here'</span>);
      }

      <span class="hljs-comment">// Use the combined data (metadata and HTML) to construct the article object</span>
      <span class="hljs-keyword">if</span>(getTitle(data) &amp;&amp; getDescription(data) &amp;&amp;
         getPublishedDate(data) &amp;&amp; getImageURL(data) &amp;&amp;
         getPlatform(data) || (item.title &amp;&amp; item.description &amp;&amp;
         item.image)) {
        <span class="hljs-keyword">return</span> {
        ...item,
        id: item.id ?? <span class="hljs-number">0</span>,
        tags: item.tags ?? [],
        title: getTitle(data) || item.title || <span class="hljs-string">'No title'</span>,
        description: item.description || getDescription(data) || <span class="hljs-string">'No description'</span>,
        publishedDate: getPublishedDate(data) ?? <span class="hljs-string">'No date'</span>,
        imgUrl: getImageURL(data) || item.image || <span class="hljs-string">'/img-2.jpg'</span>,
        siteName: getPlatform(data) || data.metadata?.publisher?.name || <span class="hljs-string">'Unknown site'</span>,
        url: item.url || <span class="hljs-string">''</span>,
      } <span class="hljs-keyword">as</span> Article;
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Proper item returned'</span>);
      } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; }
    })
  );

  <span class="hljs-comment">// Filter out null values and sort the articles by published date in descending order</span>
  <span class="hljs-keyword">const</span> filteredResults = results.filter((article): article is Article =&gt; article !== <span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> sortedResults = filteredResults.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> dateA = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.publishedDate || <span class="hljs-string">''</span>).getTime();
    <span class="hljs-keyword">const</span> dateB = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.publishedDate || <span class="hljs-string">''</span>).getTime();
    <span class="hljs-keyword">return</span> dateB - dateA;
  });
  <span class="hljs-built_in">console</span>.log(sortedResults);
  <span class="hljs-keyword">return</span> sortedResults;
}
</code></pre>
<p>It maps through the articles in the articleFile, which is the JSON file with an array of objects with article URLs. For each of them, it sends a request to the URL, and from the data gotten, returns an Article object. Then, the array of objects created, <code>results</code>, is first filtered to remove null objects, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">sorted</a> in descending order by their date properties. So, the latest article shows up first.</p>
<p>It’s then assigned in the <code>HomeClient</code> component:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> fetchArticles();
</code></pre>
<p>In the <code>fetchArticles()</code> code above, you can see that some other functions were used to extract the properties from the URLs, and assign them. Also, during deployment, I found that Substack couldn’t be accessed by the server, so I’m going to add code to allow creation of Article objects from an RSS feed. That will be in the <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio/tree/main/01-simple-blog">project repository</a>.</p>
<p>Now, let's talk about the other functions.</p>
<h4 id="heading-the-gettitle-function"><strong>The</strong> <code>getTitle()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTitle</span>(<span class="hljs-params">data:<span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span>(!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Title Loading . . .'</span>;

    <span class="hljs-keyword">if</span> (data?.html) {
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> ogTitle = $(<span class="hljs-string">'meta[property="og:title"]'</span>).attr(<span class="hljs-string">'content'</span>) || $(<span class="hljs-string">'title'</span>).text();
        <span class="hljs-keyword">return</span> ogTitle;
    }

        <span class="hljs-keyword">return</span> <span class="hljs-string">'The Title of The Article'</span>;
    }
</code></pre>
<p>This is a very simple function. It takes the <code>data</code> parameter, and if there's no data, it returns <code>Title loading . . .</code>. But if there is data, it checks to see if there's HTML in the data. If there is, it then uses cheerio to load the HTML text and extract the title from the Open Graph <code>title</code> metadata or from the <code>&lt;title&gt;</code> tag in the HTML header. Else, it returns <code>The Title of the Article</code>.</p>
<p>Here, we use jQuery-like syntax <code>$</code> to select the HTML elements, like in <code>$('title')</code>. The data taken as a parameter is the response gotten from a HTTP request to the article's URL.</p>
<h4 id="heading-the-getdescription-function"><strong>The</strong> <code>getDescription()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDescription</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Description Loading . . .'</span>;

  <span class="hljs-keyword">if</span> (data?.metadata || data?.html) {
    <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html || <span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> description = data?.metadata?.description ?? $(<span class="hljs-string">'meta[property="og:description"]'</span>).attr(<span class="hljs-string">'content'</span>) ?? <span class="hljs-string">'No description found'</span>;
    <span class="hljs-keyword">return</span> description;
  }

  <span class="hljs-keyword">return</span> <span class="hljs-string">'No description found'</span>;
}
</code></pre>
<h4 id="heading-the-geturl-function"><strong>The</strong> <code>getURL()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getURL</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span></span>{
    <span class="hljs-keyword">if</span>(!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'url'</span>;

    <span class="hljs-keyword">if</span>(data?.metadata || data?.html){
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> url = data?.metadata.url || $(<span class="hljs-string">'meta[property="og:url"]'</span>).attr(<span class="hljs-string">'content'</span>);
        <span class="hljs-keyword">return</span> url;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-string">'url'</span>;
}
</code></pre>
<p>This function is not really used to get the URL of the article for use in the object. Rather, it is used to get the URL for another function, <code>getPlatform()</code>. It works the same way as the ones we discussed before.</p>
<h4 id="heading-the-getplatform-function"><strong>The</strong> <code>getPlatform()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getURL } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getURL'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPlatform</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Platform1'</span>;

    <span class="hljs-keyword">const</span> url = getURL(data);

    <span class="hljs-keyword">if</span> (data?.html) {
      <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/^(?:https?:\/\/)?(?:www\.)?([^\/\n]+)\.(?:[a-zA-Z]{2,})/</span>;
      <span class="hljs-keyword">const</span> platform = url.match(regex);
      <span class="hljs-keyword">return</span> platform?.[<span class="hljs-number">1</span>].toUpperCase() || <span class="hljs-string">'Platform2'</span>; 
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">'Platform3'</span>;
  }
</code></pre>
<p>This function is meant to extract the name of the platform where the article is posted. I toyed with various ideas for how this should work. One of them was using the <code>siteName</code> property in the OG meta tags, but I realised from my inspection that not all platforms had it populated in a helpful way. So, the results gotten from that method would be too unpredictable.</p>
<p>So I decided to use <a target="_blank" href="https://www.freecodecamp.org/news/practical-regex-guide-with-real-life-examples/">regex (Regular Expressions)</a> to extract the site name from the URL. As you can see from the code, I didn't achieve a perfect result, but it is usable.</p>
<p>First of all, it gets the URL of the article with the <code>getURL()</code> function. Then, it uses regex:</p>
<pre><code class="lang-bash">/^(?:https?:\/\/)?(?:www\.)?([^\/\n]+)\.(?:[a-zA-Z]{2,})/
</code></pre>
<p>Here, <code>/</code> and <code>/</code> at the beginning and end are to start and end the regex string. The caret <code>^</code> marks the beginning of a line.</p>
<p>Then, we have four groups <code>()()()()</code>. The first one is a non-captured group <code>(?: )</code>. That means whatever text that matches that should be grouped together in a string, but should not be captured to be assigned to the variable. It captures any text with a 'http' in it, with or without the s <code>s?</code>, and with two slashes after. The forward slashes were escaped with backward slashes so they can be recognised as literal characters. Then, the whole group itself is made optional by adding the question mark after it <code>(...)?</code>. So, whether such a group is matched or not, the code works.</p>
<p>The second group is also a non-capturing group, also denoted by <code>?:</code> being the first thing inside the bracket. This one matches any 'www.' in the string. It's also optional. A URL may not necessarily be written with it.</p>
<p>The third group is a capturing group as it doesn't have <code>?:</code> inside the brackets. Rather, it has a character class in it <code>[]</code>. But it's a negated class <code>[^ ]</code>. It makes sure that the class does not contain a newline character <code>n</code> (the newline character n is not a string of letter n – that's why it is escaped) or a forward slash <code>/</code>, because a URL is supposed to be one line, and not multiple lines. The <code>+</code> means one or more characters, <code>([^\/\n]+)</code>. Whatever is in this group will get captured in the variable.</p>
<p>Then, the next one matches a dot (it is escaped with a backslash <code>\.</code>). After that is the last group which is also non-capturing and matches any character which is alphanumeric, capital or small letter <code>[a-zA-Z]</code>, that occurs more than two times <code>{2, }</code>.</p>
<p>So, if we have '<a target="_blank" href="https://www.linkedin,com'">https://www.linkedin,com'</a> we would have an array of captured groups ['<a target="_blank" href="https://www.linkedin.com','https://','www.','linkedin','com'%5D">https://www.linkedin.com','https://','www.','linkedin','com']</a>. Group 1 = 'https://', group 2 = 'www.', group 3 ='linkedin', group 4 = 'com'. But since only group 3 is a captured group, others will be discarded, and we have an array with only two items, the full string, and the captured group: ['<a target="_blank" href="https://www.linkedin.com','linkedin'%5D">https://www.linkedin.com','linkedin']</a><em>.</em></p>
<p>So, here, we return the second item in the array. The first item is always the full string we matched.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> platform?.[<span class="hljs-number">1</span>].toUpperCase()
</code></pre>
<p>This doesn't account for sub domains, though. This is tricky because sometimes you want to use the name of the subdomain (as in my Substack), and sometimes you want to use the name of the domain. So, I left it like that.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947172558/7f0f47c0-a1d5-4db7-a8e5-d45d1116aaac.png" alt="Image of an article with platform showing THENETWORKBITS.SUBSTACK" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-the-getimageurl-function"><strong>The</strong> <code>getImageURL()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getImageURL</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'/img-2.jpg'</span>; 

    <span class="hljs-keyword">if</span> (data?.metadata || data?.html) {
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> ogImage = $(<span class="hljs-string">'meta[property="og:image"]'</span>).attr(<span class="hljs-string">'content'</span>) || data?.metadata.image;
        <span class="hljs-keyword">return</span> ogImage || <span class="hljs-string">'/img-2.jpg'</span>; 
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">'/img-2.jpg'</span>; 
}
</code></pre>
<p>This function works just like others, and obtains the cover image URL from either the Open Graph image meta tag <code>$('meta[property="og:image"]').attr('content')</code> or <code>||</code> the image property in the JSON-LD data <code>data?.metadata.image</code>.</p>
<h4 id="heading-the-getpublisheddate-function"><strong>The</strong> <code>getPublishedDate()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPublishedDate</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{

  <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Date'</span>;

  <span class="hljs-keyword">const</span> publishedDate = data?.metadata?.datePublished;

  <span class="hljs-keyword">if</span> (publishedDate) {
    <span class="hljs-keyword">const</span> options: <span class="hljs-built_in">Intl</span>.DateTimeFormatOptions = { year: <span class="hljs-string">'numeric'</span>, month: <span class="hljs-string">'long'</span>, day: <span class="hljs-string">'numeric'</span> };
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(publishedDate).toLocaleDateString(<span class="hljs-string">'en-US'</span>, options);
  }

  <span class="hljs-keyword">if</span> (data?.html) {
    <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
    <span class="hljs-keyword">const</span> ogPublishedTime = $(<span class="hljs-string">'meta[property="article:published_time"]'</span>).attr(<span class="hljs-string">'content'</span>) ||
                            $(<span class="hljs-string">'meta[property="og:published_time"]'</span>).attr(<span class="hljs-string">'content'</span>) || 
                            $(<span class="hljs-string">'meta[name="pubdate"]'</span>).attr(<span class="hljs-string">'content'</span>);

    <span class="hljs-keyword">if</span> (ogPublishedTime) {
      <span class="hljs-keyword">const</span> options: <span class="hljs-built_in">Intl</span>.DateTimeFormatOptions = { year: <span class="hljs-string">'numeric'</span>, month: <span class="hljs-string">'long'</span>, day: <span class="hljs-string">'numeric'</span> };
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(ogPublishedTime).toLocaleDateString(<span class="hljs-string">'en-US'</span>, options);
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-string">'Date'</span>;
}
</code></pre>
<p>This function is especially useful because of the need to convert the date from the ISO 8601 format (2025-04-07T10:47:19+00:00) to the more readable format I want (April 7, 2025). Here, I used the <code>.toLocaleDateString()</code> JavaScript function to make it work (see the (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString">MDN</a>).</p>
<h3 id="heading-7-create-your-json-file">7. Create your JSON file</h3>
<p>Now, remember that we're building this to be able to pull URLs from a JSON file to put together and render the web page. That JSON file is the starting point of everything. I believe by now you're getting an error concerning that. So we need to create the JSON file.</p>
<p>In the <code>app/</code> directory, create a new file and name it <code>articles.json</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947297567/ed6b282b-971e-428b-a7b6-9d6bf5e44520.png" alt="The app/ directory" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then populate it like in this file below – an array of objects with id, URL, tags, and so on. Even though we are not trying to get the title, description, and everything from this file directly, I put in that feature. If you go back to our <code>fetchArticles()</code> function, you'll see that for most of the properties, whatever you write here will override what was gotten from the URLs.</p>
<p>It was partly a fail-safe because I thought that LinkedIn would block all requests, and as you can see from my blog already, some description tags were not well organized. So, we can replace them later with a cleaner description just by modifying this file.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"articles"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://thenetworkbits.substack.com/p/an-overview-of-json"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Cloud"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://websecuritylab.org/how-safe-is-public-wi-fi-a-network-engineer-explains/"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cybersecurity"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://www.freecodecamp.org/news/automate-cicd-with-github-actions-streamline-workflow/"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"DevOps"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        }
    ]
}
</code></pre>
<p>Here, we have an "articles" object with an array of objects, each of which have "id", "url", "tags", "title", "description", and "image" properties. You don't necessarily need the values of all of these except the ID and URL, but the keys have to be there to prevent errors.</p>
<h3 id="heading-8-add-the-finishing-touches">8. Add the finishing touches</h3>
<p>Now you can add your own favicon in the app directory. It could be a 24px by 24px file, or 48px by 48px file. It doesn't necessarily have to be in the app directory or be an icon file or be named 'favicon' – but I did it that way. You can just add this in the HTML header of your layout.tsx file which is your Next.js version of <code>index.html</code>. The favicon is the icon that shows on the tab in your browser when you open the page.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"any"</span> /&gt;</span>
</code></pre>
<p>You can also read the Next.js documentations on that here: <a target="_blank" href="https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons">Metadata Files: favicon, icon, and apple-icon | Next.js</a>. Then add your images to your <code>public/</code> directory. Be sure to name them correctly, and reference them correctly.</p>
<p>Now, if your development server was down, spin it up again to see your end results!</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947429494/1134babc-6b26-4f88-99df-7c3c8a2e8ba6.png" alt="The blog above the fold" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947441422/5b87e888-a040-4ca1-bc27-7d09e50561ce.png" alt="Articles on the blog" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947455924/64e57b54-feb9-46c9-99c9-8a7260aff45d.png" alt="About page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you've read this far, then you must be really interested in seeing the results of all this :) I already have that covered. <a target="_blank" href="https://chidiadi-portfolio.vercel.app/">Here's the blog</a>. You can go through it and interact with it.</p>
<p>Also, <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio/tree/main/01-simple-blog">this is the codebase</a>. Feel free to fork it, clone it, and interact with it as well. If you enjoyed the article, please share it with others. You can also connect with me on <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or <a target="_blank" href="https://x.com/chidiadi01">X</a>. Thanks for reading.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automate CI/CD with GitHub Actions and Streamline Your Workflow ]]>
                </title>
                <description>
                    <![CDATA[ CI/CD stands for Continuous Integration and Continuous Delivery. It is a system or set of processes and methodologies that help developers quickly update codebases and deploy applications. The Continuous Integration (CI) part of CI/CD means that deve... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automate-cicd-with-github-actions-streamline-workflow/</link>
                <guid isPermaLink="false">67fd2db6c741f3f1aec5ecd2</guid>
                
                    <category>
                        <![CDATA[ github-actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ workflow ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Mon, 14 Apr 2025 15:45:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744638276204/5cf04403-6bf0-4bf1-b9d3-89722bd90425.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>CI/CD stands for Continuous Integration and Continuous Delivery. It is a system or set of processes and methodologies that help developers quickly update codebases and deploy applications.</p>
<p>The Continuous Integration (CI) part of CI/CD means that developers can always integrate or merge their changes into the shared repository without breaking anything. Continuous Delivery, on the other hand, means that the code changes are automatically prepared for release after testing and validation.</p>
<p>CI/CD primarily involves various stages like building, testing, staging and deployment.</p>
<ul>
<li><p><strong>Build phase:</strong> This is where the code and its dependencies are compiled into a single executable. This is the first phase of Continuous Integration, and is triggered by an event like pushing code to the repository.</p>
</li>
<li><p><strong>Test phase:</strong> Here, the built artifacts are tested to be sure that the code runs as expected.</p>
</li>
<li><p><strong>Staging:</strong> Here, the application is run in a production-like environment so as to be sure it is production ready.</p>
</li>
<li><p><strong>Deployment:</strong> Here, the application is automatically deployed to the end-users.</p>
</li>
</ul>
<p>In this article, I’m going to explain how GitHub Actions works. I’ll also talk about basic GitHub Actions concepts, and then we’ll use it to build an example CI/CD pipeline.</p>
<h2 id="heading-what-is-github-actions">What is GitHub Actions?</h2>
<p>GitHub Actions is a service or feature of the GitHub platform that lets developers create their own CI/CD workflows directly on GitHub. It runs jobs on containers hosted by GitHub. The tasks are executed as defined in a YAML file called a workflow. This workflow file has to live on the <em>.github/workflows</em> folder on the repository for it to work.</p>
<h2 id="heading-basic-github-actions-concepts">Basic GitHub Actions Concepts</h2>
<p>GitHub Actions consists of events, jobs, tasks, runners, workflows, and various other features. Here is a brief explanation of the main concepts:</p>
<p><strong>Events:</strong> An event is basically something that happened. With GitHub, an event can be a push (when you push your code to the repository), a pull request, or even a cron job. These events trigger the CI/CD process.</p>
<p><strong>Tasks:</strong> When you use CI/CD, you want to be able to trigger an activity that should be done automatically. That activity is known as a task or step in GitHub. It could be building your code or testing it or deploying it.</p>
<p>Each of those tasks has to be defined by commands. A GitHub Actions task usually consists of the name, and the instructions on what to do in the form of a command which starts with  <code>- run:</code> or an Action which starts with <code>- uses:</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">steps:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Node.js</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">node-version:</span> <span class="hljs-number">16</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tests</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">project</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"Deploy step goes here"</span>
</code></pre>
<p><strong>Runner:</strong> A GitHub runner is a server that runs your tasks. It executes what is defined in your GitHub workflow. You can use your own runners or you can use the GitHub runners.</p>
<p><strong>Job:</strong> A job is a collection of steps that are being executed on the same runner. Jobs are defined in a file called the workflow.</p>
<p><strong>Workflow:</strong> The GitHub workflow is a series of jobs defined in a YAML file, that are triggered upon an event. The events do not trigger individual tasks. They can only trigger workflows. Then the tasks in the jobs of the workflow are executed.</p>
<p><strong>Contexts:</strong> These provide a way to access information about workflows, jobs, and environments in GitHub. They are accessed with the expression <code>$${{ &lt;context&gt; }}</code>. Examples include <code>github</code>, <code>env</code>, <code>vars</code>, and <code>secrets</code>. The <code>github</code> context is used to access information about the workflow. For example:</p>
<pre><code class="lang-yaml"><span class="hljs-string">$${{github.repository}}</span> <span class="hljs-comment"># should tell the name of the repository</span>

<span class="hljs-string">$${{github.actor}}</span>  <span class="hljs-comment"># should tell the username of user that initially triggered the workflow</span>
</code></pre>
<p><strong>Secrets:</strong> This is used to store and access sensitive information that’s used by, and is available to, the workflow. Secrets are redacted when printed to the log. An example is $${{secrets.GITHUB_TOKEN}}.</p>
<h2 id="heading-how-to-build-a-simple-cicd-pipeline">How to Build a Simple CI/CD Pipeline</h2>
<p>Here, we’re going to build an example workflow to deploy a simple HTML and CSS website to GitHub Pages. Follow the steps below:</p>
<ol>
<li><p>Go to the sample code in my repository and fork it from <a target="_blank" href="https://github.com/chidiadi01/StaticPage-Starter">here</a>.</p>
</li>
<li><p>Go to the settings tab in the GitHub repository:</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744220928970/d2f62ae0-49be-4770-b931-59e5bc28e20e.png" alt="Settings tab" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li><p>Go to the Pages settings:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744220974335/4aeac1df-be0d-493d-98d3-fb9ea4d48ca0.png" alt="Pages settings menu" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Set the deployment source to the <code>main</code> branch:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744290941501/365d8b5d-1265-42be-9de4-d3a07b984736.png" alt="Setting deployment source to main branch in GitHub pages" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Go to the General Actions settings and scroll down to the bottom:</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744221468786/d94fe477-65a3-49dc-85ea-6ab8cb2f9c63.png" alt="Find General Actions setting" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p> At the bottom, set the Workflow permissions to read and write:</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744221589612/4f5b1cf2-8343-4da8-ad61-21ba76319ffc.png" alt="Set workflow permissions to read and write" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ol start="7">
<li><p>In the GitHub repository, you can clone it to your PC or press the fullstop (.) on your keyboard to open GitHub Codespaces, the online version of VS Code.</p>
</li>
<li><p>Go to the sidebar and click on create a new file:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744292745424/a3ca0a1e-13cf-4182-8425-9c3500e01e3d.png" alt="Creating new file" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Create a workflows folder and file. You can call it <code>deploy.yaml</code>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744292931628/300665a5-d517-46b1-9b30-21dd4bc228a6.png" alt="Creating a workflows folder and file named deploy.yaml" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
<li><p>Copy this code into the file:</p>
</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Static</span> <span class="hljs-string">HTML</span> <span class="hljs-string">and</span> <span class="hljs-string">CSS</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>

<span class="hljs-comment"># Trigger the workflow on push to the main branch</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
<span class="hljs-comment"># Define what operating system the job should run on</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">write</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-comment"># Step 1: Checkout the repository</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">Code</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

    <span class="hljs-comment"># Step 2: Check the files that have been checked out</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Display</span> <span class="hljs-string">files</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">ls</span>

    <span class="hljs-comment"># Step 3: Deploy to GitHub Pages</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">peaceiris/actions-gh-pages@v4</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">github_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">publish_dir:</span> <span class="hljs-string">./</span> <span class="hljs-comment"># The HTML and CSS files lie in the root directory, hence that should be the publish directory</span>
</code></pre>
<ol start="11">
<li>Commit the code. You should see the job running when you go back to the repo:</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744292376966/93d6078c-02c3-41f4-a4b5-09639522bbbe.png" alt="Running job" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>When you’re done, go back to the home page of the repository and click on the Deployments section. There, you will see the GitHub Pages link to the deployment:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744293345646/e523a2b9-73a6-4ecf-9df8-99e03b457ad1.png" alt="GitHub Pages link" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>When you’re done, your repository should look like <a target="_blank" href="https://github.com/chidiadi01/StaticPage-Final">this</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned about how the CI/CD process works. We also covered the basic concepts of GitHub Actions. Finally, we created an example CI/CD pipeline with GitHub Actions. If you enjoyed this article, share it with others. You can also reach me on <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or <a target="_blank" href="https://x.com/chidiadi01">X</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is ECS Monitoring? Explained With Examples ]]>
                </title>
                <description>
                    <![CDATA[ Amazon Elastic Container Service (ECS) is a container orchestration service provided by Amazon Web Services (AWS). It is a solution developed by AWS to take care of the problem of managing large clusters of containers. Why Use ECS? There are other co... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/ecs-monitoring-explained-with-examples/</link>
                <guid isPermaLink="false">66f14c6a5d804c0e685174ed</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Mon, 23 Sep 2024 11:09:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727103254033/39d8dac3-4e18-46dc-8ad6-129386a165b3.avif" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Amazon Elastic Container Service (ECS) is a container orchestration service provided by Amazon Web Services (AWS). It is a solution developed by AWS to take care of the problem of managing large clusters of containers.</p>
<h2 id="heading-why-use-ecs">Why Use ECS?</h2>
<p>There are other container orchestration tools, each with its merits, but ECS is made for AWS and provides seamless integration into the AWS ecosystem. You can use it with AWS Elastic Load Balancer (ELB), AWS Identity and Access Management (IAM), AWS CloudTrail, store persistent data in the AWS Elastic Block Store, or monitor it with AWS CloudWatch.</p>
<p>You can also use it with AWS Fargate, which is a serverless compute engine that provides fully managed containers.</p>
<h2 id="heading-what-is-monitoring"><strong>What is Monitoring?</strong></h2>
<p>Monitoring is the process of tracking and observing the performance, availability and overall health of your resources, services, and applications. This helps to detect and troubleshoot issues before they impact users, improve application reliability and availability, optimize resource utilization and enhance the security of your applications.</p>
<p>It’s just a way to make sure all the infrastructure is running the way it is supposed to. It also helps you to know how much traffic your app is receiving if it’s a web app or website, and what is really going on with it.</p>
<p>There are different aspects of monitoring, some of which include:</p>
<ul>
<li><p>Performance monitoring: Here, we track and monitor the performance metrics of infrastructure, such as CPU usage, memory consumption, disk, I/O, and networks, and so on.</p>
</li>
<li><p>Error and log monitoring: Here, we collect and analyze logs and error messages.</p>
</li>
<li><p>Availability monitoring: We ensure that the systems are up and running.</p>
</li>
<li><p>Security monitoring: We also track and monitor security-related events and activities to respond to potential threats and vulnerabilities. Monitoring can help you detect things like a DoS attack by identifying unusual patterns in incoming traffic.</p>
</li>
</ul>
<h2 id="heading-what-do-you-monitor-on-ecs">What Do You Monitor on ECS?</h2>
<p>In cloud monitoring, metrics are used to monitor the health and performance of the infrastructure. They are used together with dimensions. Metrics are the data points collected and monitored to measure the performance, health, and usage of your cloud resources and services.</p>
<p>Dimensions are attributes and characteristics that help to filter, categorize and give context to metrics. They are represented in the form of key/value pairs.</p>
<p>Amazon ECS provides various metrics for monitoring resources. Some of them include:</p>
<ul>
<li><p><strong>CPUReservation:</strong> This is the percentage of CPU units reserved by running tasks.</p>
</li>
<li><p><strong>MemoryReservation:</strong> Percentage of memory reserved by running tasks.</p>
</li>
<li><p><strong>CPUUtilization</strong>: This is the percentage of CPU units used by running tasks.</p>
</li>
<li><p><strong>MemoryUtilization:</strong> This is the percentage of memory used by running tasks.</p>
</li>
<li><p><strong>ContainerInstances:</strong> This is the number of container instances in the cluster.</p>
</li>
<li><p><strong>RunningTasksCount:</strong> The number of tasks that are running currently in the cluster.</p>
</li>
</ul>
<p>Amazon ECS also provides dimensions, some of which are:</p>
<ul>
<li><p><strong>ContainerName:</strong> The name of the container.</p>
</li>
<li><p><strong>ClusterName:</strong> The name of the ECS cluster.</p>
</li>
<li><p><strong>ServiceName:</strong> The name of the service</p>
</li>
<li><p><strong>ServiceNameSpace:</strong> The namespace used to group a set of services in a cluster.</p>
</li>
<li><p><strong>InstanceType:</strong> This refers to the type of EC2 instance used. For example: t2.micro, c4.large, r5.xlarge, and so on.</p>
</li>
<li><p><strong>TaskID:</strong> The unique identifier assigned to each task.</p>
</li>
</ul>
<p>In AWS ECS, you can monitor your resources at different levels. For example, at the cluster level, you can look at things like CPUUtilization, CPUReservation, MemoryUtilization, and MemoryReservation. At the service level, you can see things like CPUUtilization and MemoryUtilization.</p>
<h2 id="heading-how-do-you-monitor-ecs">How Do You Monitor ECS**?**</h2>
<p>You can do this by making use of:</p>
<p><strong>Amazon CloudWatch:</strong> This is a monitoring service by AWS that allows you to collect, analyze and visualize data from your AWS resources. It also helps you to set up alarms and be notified when a threshold is reached.</p>
<p><strong>AWS Management Console:</strong> You can also view your cluster or service metrics directly on the management console.</p>
<p><strong>The ECS API:</strong> The ECS API provides programmatic access to the ECS service so you can use it to create, modify and monitor clusters and resources from outside AWS.</p>
<p><strong>Third party tools:</strong> There are third-party tools like Datadog, Prometheus, and others that can be used to monitor these metrics. Some of them will work seamlessly with AWS, some will require you to install an agent.</p>
<p>For monitoring of ECS on EC2 instances, you have direct access to the underlying EC2 instances, and can use traditional server monitoring tools to monitor metrics on the OS. For ECS on Fargate however, you don't have access to the EC2 instances.</p>
<h3 id="heading-how-to-monitor-ecs-clusters-with-aws-cloudwatch">How to Monitor ECS Clusters With AWS CloudWatch</h3>
<p>With AWS CloudWatch, you can monitor your ECS clusters in many ways, from metrics to logs to setting up alarms. In this guide, I’ll show you how to use the Automatic Dashboards to view ECS cluster metrics. To do that, follow these steps:</p>
<ol>
<li>From the AWS Management Console, open CloudWatch and go to Dashboards on the sidebar.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726955177185/348508ff-c765-4962-bb7a-d61fdb11f496.png" alt="Dashboard image" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ol start="2">
<li>Click on the Automatic Dashboards tab.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726955263446/9fd7ffbb-e906-4db2-be57-f3dc85406a4b.png" alt="Automatic dashboard image" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Click on ECS Cluster. This will take you to the dashboard where you’ll see the pre-configured metrics for your ECS clusters.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726955373129/cf2a20fa-06b3-4ead-a454-849bfb12bba1.png" alt="Click on the ECS Cluster" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726955502141/12a0e538-8983-42be-825d-343ddf840625.png" alt="ECS Cluster dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can also expand individual metrics.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726955515843/eafca64b-fe22-4580-8243-8e55e3e92496.png" alt="Task count metric" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-wrapping-up"><strong>Wra</strong>pping Up</h2>
<p>In this article, you learned about ECS monitoring. You learned about the different metrics you can monitor and how to monitor them.</p>
<p>Lastly, you learned how to monitor ECS clusters using AWS CloudWatch.</p>
<p>Thanks for reading. You can connect with me on <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or follow me on <a target="_blank" href="https://x.com/chidiadi01">X</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ CLI Shells – A Brief History of Human-Computer Interfaces ]]>
                </title>
                <description>
                    <![CDATA[ By Chidiadi Anyanwu A computer is basically a piece of electronic circuitry that performs tasks as instructed by its users. But for a human to interact with this hardware, they must really know and understand how it works. The person must also know t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/shells-a-history-of-human-computer-interfaces/</link>
                <guid isPermaLink="false">66d45dd7868774922c884fcd</guid>
                
                    <category>
                        <![CDATA[ cli ]]>
                    </category>
                
                    <category>
                        <![CDATA[ command line ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Computer Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ shell ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Tue, 18 Jul 2023 16:45:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/Evernote-cli-geeknote.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Chidiadi Anyanwu</p>
<p>A computer is basically a piece of electronic circuitry that performs tasks as instructed by its users.</p>
<p>But for a human to interact with this hardware, they must really know and understand how it works. The person must also know the order in which to give the computer various tasks to produce a meaningful result.</p>
<p>But most of us don't know these things. What happened?</p>
<p>In this article, we're going to look at:</p>
<ul>
<li><p>The history of early computing</p>
</li>
<li><p>How operating systems were developed</p>
</li>
<li><p>Modern operating systems</p>
</li>
<li><p>The development of kernels and shells</p>
</li>
<li><p>The history of shells</p>
</li>
<li><p>Why CLI tools are still important</p>
</li>
</ul>
<h2 id="heading-the-early-days-of-computers">The Early Days of Computers</h2>
<p>In the 1800's, computers were mostly used to work on large amounts of numerical data. They were basically programmable calculators the size of small factories.</p>
<p>The data they worked on were also very physical. They were manually fed as punched cards into the machine's card readers. The computers were then programmed by physically rewiring cables, plugboards, and switches to determine what operations would be carried out on the input data. Some of the computers even processed logic through partially mechanical means.</p>
<p>Plugboards allowed you write (or wire) a program and store it so when you needed that program, you'd remove the current plugboard and install a new one. The output of the programs were printed out by line printers, saved on tapes, or punched on cards.</p>
<p>These programming and memory technologies were used in many forms of technological designs.</p>
<p>For example, the Enigma machine which was used to scramble letters and encipher top secret military and diplomatic communication had a plugboard you would rewire to change settings.</p>
<p>The IBM International Daily Dial Attendance Recorder also counted and recorded staff attendance on punched cards. These were more special-purpose equipment.</p>
<h2 id="heading-early-operating-systems">Early Operating Systems</h2>
<p>The first-ever speech synthesis was done in 1962 on an IBM 704, a computer without an operating system.</p>
<p>At the time of the first computers, nobody ever thought of operating systems. People wrote their programs in machine language (or wired them on plugboards) and ran them. There was no way to run two programs at the same time or perform error detection/correction. Your program ran until it either crashed or completed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/IBM_Electronic_Data_Processing_Machine_-_GPN-2000-001881-IBM-704.jpg" alt="Picture of a man and a woman operating the IBM 704 in 1957." width="600" height="400" loading="lazy"></p>
<p><em>IBM 704 computer with two operators in 1957</em></p>
<p>Initially, computers didn't ship with any software. Later on, IBM included FORTRAN and COBOL compilers in their mainframes. Then came 'resident monitors,' the precursor to operating systems. They literally got the name because they were software that resided in the company's memory and monitored tasks. They basically performed tasks like cleaning up after programs and helping to sequence jobs on the computers.</p>
<p>The first operating systems were made by computer owners who were tired of under-utilizing their computers. They didn't want to have to wait for a program to complete before they manually loaded in another set of data and programs.</p>
<p>One of these early operating systems was the Input/Output System of General Motors and North American Aviation (GM-NAA I/O). Its main aim was to execute the next program automatically after the current one was finished (batch processing). It was created in 1956, and is known as the first-ever working operating system.</p>
<p>IBM later modified one of its customer's operating systems and distributed it as IBSYS. Then, IBM thought that it wasn't good for different computers to have different (machine language) instruction sets, so they stopped all production and started a new line called System/360.</p>
<p>With this, they aimed to build just one operating system for all their computers. It didn't quite go as planned, so they ended up with a family of operating systems, OS/360.</p>
<p>OS/360 included features like time-sharing, error detection/correction, and device management, which are all features implemented in modern operating systems. The OS/360 was introduced in 1964 and was in use until 1977.</p>
<p>Early time-sharing technologies enabled multiple people to access the same mainframe from their different terminals at once. The first computer terminals were teletypewriters (or teletypes). Teletypes were developed around the 1830's but weren't used as computer terminals until the 1970's.</p>
<h2 id="heading-modern-operating-systems">Modern Operating Systems</h2>
<p>The MULTiplexed Information and Computing Service (MULTICS) operating system was initially developed for General Electric's 645 mainframe, and later Honeywell's mainframes when they bought over GE's computer division.</p>
<p>It was jointly developed in 1964 by GE, MIT, and Bell Labs, mainly as a successor to the Compatible Time-Sharing System (CTSS).</p>
<p>It came with a lot of new ideas to the computing world like access control, dynamic linking, support for ASCII, and dynamic reconfiguration.</p>
<p>Dynamic reconfiguration allowed the operators to remove and add memories and CPUs without logging out or disrupting users. With the advent of the Compatible Time-Sharing System (CTSS), computers were looked at as a utility.</p>
<p>The idea was that computers were too big and expensive and could be used by only one person at a time. But what if that expensive hardware could be used more efficiently by allowing more than one person to use them at the same time? That way, the computer could become a public utility, accessed from home with terminals.</p>
<p>With a public utility, you need to be able to carry out maintenance on different components without disrupting the service. That was the usefulness of dynamic reconfiguration.</p>
<p>That idea of computers becoming a public utility seems to have lived on with the use of servers, and now, cloud computing.</p>
<p>At a point, MULTICS didn't work out and the companies went their separate ways. Ken Thompson, one of the programmers, later described MULTICS as 'over-designed,' 'overbuilt,' and close to unusable. In an interview with Peter Seibel, he said that the things he liked enough to take from it were the hierarchical filesystem and the shell.</p>
<p>He later went on to create the UNIX operating system with Dennis Ritchie.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Timeline-Computers.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-the-development-of-kernels-and-shells">The Development of Kernels and Shells</h2>
<p>One of the major features of the UNIX operating system was the splitting of the operating system into shell and kernel. The OS kernel was then meant to handle all the system calls, while the shell was used to access the system's services without exposing the inner workings of the operating system.</p>
<p>This concept was first introduced by French computer scientist Louis Pouzin in 1965. He also coined the term shell. This is how he described it in a document titled, <a target="_blank" href="https://people.csail.mit.edu/saltzer/Multics/Multics-Documents/MDN/MDN-4.pdf">The SHELL: A Global Tool for Calling and Chaining Procedures in the System</a>.</p>
<blockquote>
<p>We may envision a common procedure called automatically by the supervisor whenever a user types in some message at his console . . . we shall refer to that procedure as the "SHELL".</p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Louis-Pouzin-Shell-Document-Definition.png" alt="Louis Pouzin Shell Document Definitionpng" width="600" height="400" loading="lazy"></p>
<p><em>Louis Pouzin's Document on Shells</em></p>
<p>According to the document, when the user types in a command, the supervisor initiates a new stack to save the command, then calls the shell with the command as an argument.</p>
<p>He also envisioned a future where people created their own shells and used them without having to meddle with the underlying structure of the shell.</p>
<blockquote>
<p>"An important facility is that the SHELL being itself a common procedure may be replaced by a private one supplied by the user. On that way, not only a particular procedure can be replaced on user's choice, but all conventions about typing commands may be tailor-made to user's wishes just by providing his own SHELL."</p>
</blockquote>
<p>This idea was first implemented in MULTICS, and is one of the only things Ken Thompson admitted to liking enough to take from the project.</p>
<p>Now, in modern operating systems, the shell is the software that lets you communicate with the operating system, and access the OS services which are things like program execution, file management, and I/O management. It could be a command-line shell (CLI) or it could be a graphical user interface (GUI).</p>
<p>The kernel is the part that handles all the system calls, manages computer resources like memory, CPU, and storage, and handles timesharing between processes.</p>
<h2 id="heading-history-of-cli-shells">History of CLI Shells</h2>
<p>Shells as we know them now really began with UNIX, and one of the first was the Thompson shell.</p>
<h3 id="heading-thompson-shell-1971">Thompson Shell (1971)</h3>
<p>Ken Thompson, of course, went on to create his own shell. It was a simple command interpreter, not designed for scripting. It introduced the &lt; &gt; symbols for redirecting the input or output of a command, and the | symbol for piping.</p>
<h3 id="heading-c-shell-1978">C Shell (1978)</h3>
<p>The C Shell (or csh) was created by Bill Joy and enabled scripting. The objective was to create a command language that looked more like the C programming language.</p>
<h3 id="heading-bourne-shell-1979">Bourne Shell (1979)</h3>
<p>Stephen Bourne started work on the Bourne Shell in 1976 as a replacement for the Thompson Shell. It was intended as a scripting language. One of the major goals was to enable using scripts as filters.</p>
<h3 id="heading-korn-shell-1983">Korn Shell (1983)</h3>
<p>Developed by David Korn, the Korn Shell was based on source code from the Bourne Shell, and attempted to integrate the features of C Shell and Bourne Shell. It is <a target="_blank" href="https://en.wikipedia.org/wiki/POSIX">POSIX.2</a> compliant.</p>
<h3 id="heading-bourne-again-shell-aka-bash-1989">Bourne Again SHell aka BASH (1989)</h3>
<p>BASH was written by Brian Fox in 1989 and released under the GNU license as a free and open-source version of the Bourne Shell.</p>
<p>It was one of the first programs Linus Torvalds ported to Linux and is the default shell for most Linux distributions. It was built for scripting and is <a target="_blank" href="https://en.wikipedia.org/wiki/POSIX">POSIX</a> compliant.</p>
<h3 id="heading-z-shell-1990">Z Shell (1990)</h3>
<p>The Z Shell (or Zsh) was created by Paul Falstad as an extended and improved Bourne Shell. The name was derived from the name of a teaching assistant at Paul's university named Zhong Shao.</p>
<p>It also enables scripting and is aesthetically superior to Bash. It supports plugins and extensions, auto-completion, and some other globing capabilities that are unavailable in Bash.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Timeline-Shells-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There are many other popular shells used today, but I'm not going to talk about them.</p>
<p>All these shells mentioned did not apply to Windows (until recently). Windows had two command-line shells: Command Prompt (cmd) and PowerShell. The PowerShell was created as an extension to Command Prompt. It supports cmdlets, scripting, piping, and many more features.</p>
<p>Then in 2019, that changed with Windows Terminal, a new terminal emulator for Windows that can run Bash on what is called Windows Subsystem for Linux (WSL). It also supports Command Prompt, PowerShell, and Azure Cloud shell out-of-the-box.</p>
<h2 id="heading-why-are-these-cli-tools-important-when-we-have-graphical-user-interfaces-guis">Why Are These CLI Tools Important When We Have Graphical User Interfaces (GUIs)?</h2>
<p>You may wonder why we would take the pains to go through this whole evolution and yet still love to use CLI tools and shells. What's so interesting about typing commands into terminals?</p>
<p>The first and most compelling reason is that CLI tools are lightweight because they're text-based. In cases where you have servers and other devices where you need to optimize the use of resources, it's not very wise to use up most of the resources to run GUI interfaces.</p>
<p>Using the CLI can also provide you with a great deal of flexibility and speed that you won't get with a GUI. For example, you may want to search for all the images in a folder with 1000 files and rename them in a certain order. Doing that with your GUI will be time-consuming and maybe frustrating. With the CLI, you can just type a few commands.</p>
<p>Scripts are another important feature. Sometimes, there are tasks you want to carry out multiple times and you don't want to have to navigate through menus all the time or even type commands multiple times. You can write scripts that you run to automatically carry out the tasks repetitively.</p>
<p>In cases where you have to access devices remotely, like web servers or network devices, the CLI is also the most favoured method. Also, for some tasks, it's just easier to do them with a few commands than using your GUI—updating your apps on Linux for example.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>We've really come a long way from purely mechanical computers to purely electronic computers. The way we interact with computers has changed over the decades, but command-line interfaces aren't going anywhere soon.</p>
<p>Thanks for reading. I hope you enjoyed this article. You can <a target="_blank" href="https://www.linkedin.com/in/chidiadi-anyanwu">connect with me on LinkedIn.</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is An ACL? Access Control Lists Explained ]]>
                </title>
                <description>
                    <![CDATA[ In computing, access control is the concept of limiting or regulating a person or machine's access to certain information or resources. One of the major mechanisms you use to do that is an access control list (ACL). An ACL is a set of rules for allow... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-deep-dive-into-access-control-lists/</link>
                <guid isPermaLink="false">66d45dd53a8352b6c5a2aa0b</guid>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ computer networking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Fri, 14 Apr 2023 14:43:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/towfiqu-barbhuiya-FnA5pAzqhMM-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In computing, access control is the concept of limiting or regulating a person or machine's access to certain information or resources.</p>
<p>One of the major mechanisms you use to do that is an access control list (ACL). An ACL is a set of rules for allowing or denying access to certain resources. Resources in this case may be files, networks, or devices.</p>
<p>In this article, we'll talk about what access control lists really are, and how you can use them. We're going to deal with:</p>
<ul>
<li><p>Filesystem ACLs and Network ACLs</p>
</li>
<li><p>Firewalls and stateful packet filtering</p>
</li>
<li><p>ACLs in Cloud Networking (Azure NSG, AWS SG, AWS NACL)</p>
</li>
<li><p>ACLs in DNS (BIND9)</p>
</li>
<li><p>ACLs in core networking (Cisco ACL types, Huawei ACL types)</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To understand this article, you need a basic understanding of networking, firewalls, and cloud computing. You may particularly need to understand basic of <a target="_blank" href="https://www.linkedin.com/pulse/ip-addressing-chidiadi-anyanwu">IP addressing</a> and <a target="_blank" href="https://www.linkedin.com/pulse/dns-deep-dive-chidiadi-anyanwu/">DNS</a> concepts.</p>
<h2 id="heading-types-of-access-control-lists">Types of Access Control Lists</h2>
<p>When we talk about ACLs, many people just think of networks. But in fact, there are two types of ACLs:</p>
<ul>
<li><p>File system ACLs</p>
</li>
<li><p>Networking ACLs</p>
</li>
</ul>
<p>Filesystem ACLs help operating systems know what the user access privileges are for different files or directories in the system. NFSv4 ACLs and POSIX ACLs are examples of filesystem ACL types.</p>
<p>Networking ACLs are applied on interfaces and you use them to allow or deny traffic from certain sources or to certain destinations. This is what I'll be focusing on in this article.</p>
<h2 id="heading-structure-of-an-acl-rule">Structure Of An ACL Rule</h2>
<p>An ACL is like a group of rules identified by a name or number. An ACL rule usually has a priority number, the criteria (source address, destination address, and so on) and the allow/deny statement.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Cisco-ACL-structure.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cisco ACL structure</em></p>
<h2 id="heading-firewalls-and-acls">Firewalls and ACLs</h2>
<p>A firewall is a security device or software that monitors the traffic going in and out of a device or network, and filters out unwanted or malicious traffic.</p>
<p>Until stateful packet inspection, ACLs were the major mechanism through which firewalls worked. With ACLs, packets are allowed and denied based on properties specified in the rules.</p>
<p>ACLs are stateless. You must create an inbound rule and a corresponding outbound rule, or else packets from one side might be blocked.</p>
<p>With stateful packet inspection (also known as dynamic packet filtering), you could then create security policies for a type of traffic. The firewall would establish a session whenever a packet is allowed, so that any response to that packet is allowed even though there was no specific policy to allow it.</p>
<p>This makes things easier and more efficient than using ACLs that are uni-directional. But it also means that more computing resources are utilized by the firewall and the network is slowed down.</p>
<p>Now, firewalls are a lot more complex than that with deep packet inspection (DPI), Intrusion Detection System (IDS)/Intrusion Prevention System (IPS) capabilities, and even antivirus capabilities, but those are outside the scope of this article.</p>
<p>Let's explore some networking situations where ACLs are used.</p>
<h2 id="heading-acls-in-cloud-networking">ACLs in Cloud Networking</h2>
<p>The major cloud service providers (CSPs) provide forms of ACLs or firewall capabilities for their customers to use in their cloud infrastructure.</p>
<p>For example, in Microsoft Azure, we have what is called Network Security Groups (NSG) and in AWS, we have Security Groups (SG) and the Network Access Control List (NACL). These are all implementations of ACL-like security.</p>
<h3 id="heading-aws">AWS</h3>
<p>An AWS security group determines what traffic is allowed to and from the resources attached to that security group. It consists of a list of inbound and outbound rules, and is stateful.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot--320--1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Default AWS Security Group</em></p>
<p>An AWS Network Access Control List is another list of rules but at the subnet level. The rules consist of the rule number, type, protocol, port range, source, destination and allow/deny fields. A NACL can be applied more than one subnet, but a subnet cannot be attached to more than one NACL.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot--319--nacl-3-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Inbound rules for AWS NACL</em></p>
<h3 id="heading-azure">Azure</h3>
<p>An Azure Network Security Group is a kind of firewall feature that works both at the subnet level and the network interface card (NIC) of the resources in your VNet. It is basically also a list of ACL rules consisting of priority number, name, port, protocol, source and destination.</p>
<p>Here, you can use IP addresses, service tags, or application security groups (ASGs) in the source and destination fields. NSGs are stateful.</p>
<p>Both the Azure NSG and the AWS NACL rules are very similar to the ACL rules used in core networking. Also, you cannot really refer to AWS Security Groups and Azure NSGs as ACLs because they're not stateless.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Microsoft-NSG-rules-image.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Azure NSG</em></p>
<h2 id="heading-acls-in-dns">ACLs in DNS</h2>
<p><a target="_blank" href="https://www.linkedin.com/pulse/dns-deep-dive-chidiadi-anyanwu/">DNS servers help resolve domain names to IP addresses</a>. If they accept and respond to requests from every device around them, it will impact their performance and make them susceptible to DDoS attacks. So, DNS administrators use ACLs to determine who can send DNS requests to the servers.</p>
<p>For example, in a BIND9 server, such an ACL will be defined in the named.conf file, and would look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot--308--1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>An ACL in BIND9</em></p>
<h2 id="heading-acls-in-core-networking">ACLs in Core Networking</h2>
<p>This is a bit more complex than the other contexts we discussed above. ACLs on network devices are configured on the interfaces, and are used in many different scenarios. There are also different types of ACLs. By network devices, I mean devices like routers, switches, firewalls, access controllers, and so on.</p>
<p>Generally, these ACLs are identified by their names or ACL numbers, and their rules follow the format:</p>
<p><em>permit/deny criteria</em></p>
<p>For Cisco devices, there are two major types of IPv4 ACLs:</p>
<ul>
<li><p>Standard access lists</p>
</li>
<li><p>Extended access lists</p>
</li>
</ul>
<h3 id="heading-standard-acls">Standard ACLs</h3>
<p>These ACLs permit or deny traffic based on only the source IP address.</p>
<pre><code class="lang-python">R1(config)<span class="hljs-comment">#access-list 10 permit 192.168.17.0 0.0.0.255</span>
</code></pre>
<p>The rule above tells the router to permit packets from the <em>192,168,17,0/24</em> subnet. Note that <em>0.0.0.255</em> is not a subnet mask. It is a wildcard that tells the device to which extent it must match the address you entered. <em>255</em> means any number goes while <em>0</em> means it must match exactly.</p>
<p>So here, the network part <em>192.168.17</em> must be exactly the same in whatever packet, while the last octet (the host part) can be whatever. You can learn more about IP addressing <a target="_blank" href="https://www.linkedin.com/pulse/ip-addressing-chidiadi-anyanwu">here</a>.</p>
<h3 id="heading-extended-acls">Extended ACLs</h3>
<p>These ACLs permit or deny traffic based on what is known in networking as the 5-tuple (source address, destination address, source port, destination port, transport layer protocol).</p>
<pre><code class="lang-python">R2(config)<span class="hljs-comment">#access-list 100 permit tcp 10.1.1.0 0.0.0.255 host 10.2.2.2 eq 80</span>
</code></pre>
<p>The command above tells the router to permit any packet using the TCP transport layer protocol, coming from the 10.1.1.0/24 network to port 80 (<a target="_blank" href="https://www.linkedin.com/pulse/http-network-protocol-chidiadi-anyanwu/">HTTP</a>) of the host, 10.2.2.2.</p>
<p>The term 5-tuple in networking probably originated from mathematics. A tuple means a record/row. 5-tuple means a row with five columns – an ordered list of 5 elements.</p>
<p>The five elements we're mostly concerned with in networking when dealing with packets are the IP addresses (source and destination), port numbers (source and destination), and transport layer protocol. So, they're usually referred to as 5-tuple.</p>
<p>ACL numbers 1 - 99 and 1300 - 1999 denote standard ACLs while numbers 100 - 199 and 2000 - 2699 denote extended ACLs.</p>
<p>Many other vendors follow this pattern, but Huawei doesn't.</p>
<p>For Huawei devices, there are 5 types of IPv4 ACLs:</p>
<ul>
<li><p>Basic ACLs ( ACL numbers 2000 - 2999)</p>
</li>
<li><p>Advanced ACLs (ACL numbers 3000 - 3999)</p>
</li>
<li><p>Layer 2 ACLs (ACL numbers 4000 - 4999)</p>
</li>
<li><p>User-defined ACLs (ACL numbers 5000 - 5999)</p>
</li>
<li><p>User ACLs (ACL numbers 6000 - 6999)</p>
</li>
</ul>
<p><strong>Basic ACL:</strong> Permits or denies traffic based on source address. The ACL number ranges from 2000 - 2999.</p>
<p><strong>Advanced ACL:</strong> Permits or denies traffic based on the 5-tuple (source IP address, destination IP address, source port, destination port, and protocol type).</p>
<p><strong>Layer 2 ACL:</strong> Permits or denies traffic based on information in the frame header (source MAC address, destination MAC address, layer 2 protocol type).</p>
<p><strong>User-defined ACL:</strong> Permits or denies traffic based on packet headers, offsets, character string masks, and user defined character strings.</p>
<p><strong>User ACL:</strong> Permits or denies traffic based on source and destination IP addresses or user control list (UCL) groups, source and destination ports, and IPv4 protocol types.</p>
<pre><code class="lang-python">acl <span class="hljs-number">3500</span>                                                
 rule <span class="hljs-number">0</span> deny tcp source <span class="hljs-number">10.1</span><span class="hljs-number">.1</span><span class="hljs-number">.0</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.255</span> destination <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.9</span> <span class="hljs-number">0</span> destination-port eq <span class="hljs-number">80</span>                                                                
 rule <span class="hljs-number">5</span> allow tcp source <span class="hljs-number">10.1</span><span class="hljs-number">.1</span><span class="hljs-number">.0</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.255</span> destination <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.9</span> <span class="hljs-number">0</span> destination-port eq telnet
</code></pre>
<h3 id="heading-implicit-deny">Implicit deny</h3>
<p>It is also important to note that even if you do not add any rule at the end of your ACL, the last rule there is always a deny rule. It is not shown, so it is implicit. But it is there. It denies any packet that does not match any rule in your ACL.</p>
<h3 id="heading-a-few-things-to-know">A Few Things To Know</h3>
<p>ACL rules are executed sequentially, so if you have rule 3 and rule 5, rule 3 gets executed first.</p>
<p>It is always a good practice to create rules at intervals (rule 10, rule 20, rule 30) rather than just serially (rule 1, rule 2, rule 3). The reason is that you may want to add a rule in-between two existing rules, and you want the system to execute it in that particular order. It saves stress if there was space for that from the beginning.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Access control is critical to security. Digitally, ACLs have been the go-to mechanism for quick and easy access control. Though other methods like role-based access control (RBAC) and attribute-based access control (ABAC) have emerged, ACL still has its place in access control.</p>
<p>Thanks for reading. If you enjoyed this article, please share it so others can see it too.</p>
<p>You can also <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">connect with me on LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
